View Javadoc
1   package org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.sql.SQLException;
23  import java.util.Hashtable;
24  import java.util.Map;
25  
26  import javax.naming.Context;
27  import javax.naming.InitialContext;
28  import javax.naming.NamingException;
29  import javax.sql.DataSource;
30  
31  import org.apache.commons.jcs3.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
32  import org.apache.commons.jcs3.log.Log;
33  import org.apache.commons.jcs3.log.LogManager;
34  
35  /**
36   * A factory that looks up the DataSource from JNDI.  It is also able
37   * to deploy the DataSource based on properties found in the
38   * configuration.
39   *
40   * This factory tries to avoid excessive context lookups to improve speed.
41   * The time between two lookups can be configured. The default is 0 (no cache).
42   *
43   * Borrowed and adapted from Apache DB Torque
44   */
45  public class JndiDataSourceFactory implements DataSourceFactory
46  {
47      /** The log. */
48      private static final Log log = LogManager.getLog(JndiDataSourceFactory.class);
49  
50      /** The name of the factory. */
51      private String name;
52  
53      /** The path to get the resource from. */
54      private String path;
55  
56      /** The context to get the resource from. */
57      private Context ctx;
58  
59      /** A locally cached copy of the DataSource */
60      private DataSource ds;
61  
62      /** Time of last actual lookup action */
63      private long lastLookup;
64  
65      /** Time between two lookups */
66      private long ttl; // ms
67  
68      /**
69       * @return the name of the factory.
70       */
71      @Override
72  	public String getName()
73      {
74      	return name;
75      }
76  
77      /**
78       * @see org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.DataSourceFactory#getDataSource()
79       */
80      @Override
81  	public DataSource getDataSource() throws SQLException
82      {
83          final long time = System.currentTimeMillis();
84  
85          if (ds == null || time - lastLookup > ttl)
86          {
87              try
88              {
89                  synchronized (ctx)
90                  {
91                      ds = (DataSource) ctx.lookup(path);
92                  }
93                  lastLookup = time;
94              }
95              catch (final NamingException e)
96              {
97                  throw new SQLException(e);
98              }
99          }
100 
101         return ds;
102     }
103 
104     /**
105      * @see org.apache.commons.jcs3.auxiliary.disk.jdbc.dsfactory.DataSourceFactory#initialize(JDBCDiskCacheAttributes)
106      */
107     @Override
108 	public void initialize(final JDBCDiskCacheAttributes config) throws SQLException
109     {
110     	this.name = config.getConnectionPoolName();
111         initJNDI(config);
112     }
113 
114     /**
115      * Initializes JNDI.
116      *
117      * @param config where to read the settings from
118      * @throws SQLException if a property set fails
119      */
120     private void initJNDI(final JDBCDiskCacheAttributes config) throws SQLException
121     {
122         log.debug("Starting initJNDI");
123 
124         try
125         {
126             this.path = config.getJndiPath();
127             log.debug("JNDI path: {0}", path);
128 
129             this.ttl = config.getJndiTTL();
130             log.debug("Time between context lookups: {0}", ttl);
131 
132     		final Hashtable<String, Object> env = new Hashtable<>();
133             ctx = new InitialContext(env);
134 
135             if (log.isTraceEnabled())
136             {
137             	log.trace("Created new InitialContext");
138             	debugCtx(ctx);
139             }
140         }
141         catch (final NamingException e)
142         {
143             throw new SQLException(e);
144         }
145     }
146 
147     /**
148      * Does nothing. We do not want to close a dataSource retrieved from Jndi,
149      * because other applications might use it as well.
150      */
151     @Override
152 	public void close()
153     {
154         // do nothing
155     }
156 
157     /**
158      *
159      * @param ctx the context
160      * @throws NamingException
161      */
162     private static void debugCtx(final Context ctx) throws NamingException
163     {
164         log.trace("InitialContext -------------------------------");
165         final Map<?, ?> env = ctx.getEnvironment();
166         log.trace("Environment properties: {0}", env.size());
167         env.forEach((key, value) -> log.trace("    {0}: {1}", key, value));
168         log.trace("----------------------------------------------");
169     }
170 }