View Javadoc

1   package org.apache.torque.dsfactory;
2   
3   /*
4    * Copyright 2001-2004 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License")
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.util.Hashtable;
20  import java.util.Iterator;
21  import java.util.Map;
22  import java.util.StringTokenizer;
23  
24  import javax.naming.Context;
25  import javax.naming.InitialContext;
26  import javax.naming.NameAlreadyBoundException;
27  import javax.naming.NamingException;
28  import javax.sql.DataSource;
29  
30  import org.apache.commons.configuration.Configuration;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  import org.apache.torque.TorqueException;
36  
37  /***
38   * A factory that looks up the DataSource from JNDI.  It is also able
39   * to deploy the DataSource based on properties found in the
40   * configuration.
41   *
42   * This factory tries to avoid excessive context lookups to improve speed.
43   * The time between two lookups can be configured. The default is 0 (no cache).
44   *
45   * @author <a href="mailto:jmcnally@apache.org">John McNally</a>
46   * @author <a href="mailto:thomas@vandahl.org">Thomas Vandahl</a>
47   * @version $Id: JndiDataSourceFactory.java,v 1.6.2.3 2004/10/20 14:13:58 henning Exp $
48   */
49  public class JndiDataSourceFactory
50      extends AbstractDataSourceFactory
51      implements DataSourceFactory
52  {
53  
54      /*** The log. */
55      private static Log log = LogFactory.getLog(JndiDataSourceFactory.class);
56  
57      /*** The path to get the resource from. */
58      private String path;
59      /*** The context to get the resource from. */
60      private Context ctx;
61  
62      /*** A locally cached copy of the DataSource */
63      private DataSource ds = null;
64  
65      /*** Time of last actual lookup action */
66      private long lastLookup = 0;
67  
68      /*** Time between two lookups */
69      private long ttl = 0; // ms
70  
71      /***
72       * @see org.apache.torque.dsfactory.DataSourceFactory#getDataSource
73       */
74      public DataSource getDataSource() throws TorqueException
75      {
76      	long time = System.currentTimeMillis();
77      	
78      	if (ds == null || time - lastLookup > ttl)
79      	{
80              try
81              {
82                  ds = ((DataSource) ctx.lookup(path));
83  	        lastLookup = time;
84              }
85              catch (Exception e)
86              {
87                  throw new TorqueException(e);
88              }
89      	}
90  
91     	return ds;
92      }
93  
94      /***
95       * @see org.apache.torque.dsfactory.DataSourceFactory#initialize
96       */
97      public void initialize(Configuration configuration) throws TorqueException
98      {
99          if (configuration == null)
100         {
101             throw new TorqueException(
102                 "Torque cannot be initialized without "
103                     + "a valid configuration. Please check the log files "
104                     + "for further details.");
105         }
106         initJNDI(configuration);
107         initDataSource(configuration);
108     }
109 
110     /***
111      * Initializes JNDI.
112      *
113      * @param configuration where to read the settings from
114      * @throws TorqueException if a property set fails
115      */
116     private void initJNDI(Configuration configuration) throws TorqueException
117     {
118         log.debug("Starting initJNDI");
119         Hashtable env = null;
120         Configuration c = configuration.subset("jndi");
121         if (c == null)
122         {
123             throw new TorqueException(
124                 "JndiDataSourceFactory requires a jndi "
125                     + "path property to lookup the DataSource in JNDI.");
126         }
127         try
128         {
129             Iterator i = c.getKeys();
130             while (i.hasNext())
131             {
132                 String key = (String) i.next();
133                 if (key.equals("path"))
134                 {
135                     path = c.getString(key);
136                     log.debug("JNDI path: " + path);
137                 }
138                 else if (key.equals("ttl"))
139 		{
140 		    ttl = c.getLong(key, ttl);
141                     log.debug("Time between context lookups: " + ttl);
142 		}
143                 else
144                 {
145                     if (env == null)
146                     {
147                         env = new Hashtable();
148                     }
149                     String value = c.getString(key);
150                     env.put(key, value);
151                     log.debug("Set jndi property: " + key + "=" + value);
152                 }
153             }
154             if (env == null)
155             {
156                 ctx = new InitialContext();
157             }
158             else
159             {
160                 ctx = new InitialContext(env);
161             }
162             log.debug("Created new InitialContext");
163             debugCtx(ctx);
164         }
165         catch (Exception e)
166         {
167             log.error("", e);
168             throw new TorqueException(e);
169         }
170     }
171 
172     /***
173      * Initializes the DataSource.
174      *
175      * @param configuration where to read the settings from
176      * @throws TorqueException if a property set fails
177      */
178     private void initDataSource(Configuration configuration)
179         throws TorqueException
180     {
181         log.debug("Starting initDataSources");
182         Configuration c = configuration.subset("datasource");
183         try
184         {
185             if (c != null)
186             {
187                 Object ds = null;
188                 Iterator i = c.getKeys();
189                 while (i.hasNext())
190                 {
191                     String key = (String) i.next();
192                     if (key.equals("classname"))
193                     {
194                         String classname = c.getString(key);
195                         log.debug("Datasource class: " + classname);
196 
197                         Class dsClass = Class.forName(classname);
198                         ds = dsClass.newInstance();
199                     }
200                     else
201                     {
202                         log.debug("Setting datasource property: " + key);
203                         setProperty(key, c, ds);
204                     }
205                 }
206 
207                 bindDStoJndi(ctx, path, ds);
208             }
209         }
210         catch (Exception e)
211         {
212             log.error("", e);
213             throw new TorqueException(e);
214         }
215     }
216 
217     /***
218      *
219      * @param ctx the context
220      * @throws NamingException
221      */
222     private void debugCtx(Context ctx) throws NamingException
223     {
224         log.debug("InitialContext -------------------------------");
225         Map env = ctx.getEnvironment();
226         Iterator qw = env.keySet().iterator();
227         log.debug("Environment properties:" + env.size());
228         while (qw.hasNext())
229         {
230             Object prop = qw.next();
231             log.debug("    " + prop + ": " + env.get(prop));
232         }
233         log.debug("----------------------------------------------");
234     }
235 
236     /***
237      *
238      * @param ctx
239      * @param path
240      * @param ds
241      * @throws Exception
242      */
243     private void bindDStoJndi(Context ctx, String path, Object ds)
244         throws Exception
245     {
246         debugCtx(ctx);
247 
248         // add subcontexts, if not added already
249         int start = path.indexOf(':') + 1;
250         if (start > 0)
251         {
252             path = path.substring(start);
253         }
254         StringTokenizer st = new StringTokenizer(path, "/");
255         while (st.hasMoreTokens())
256         {
257             String subctx = st.nextToken();
258             if (st.hasMoreTokens())
259             {
260                 try
261                 {
262                     ctx.createSubcontext(subctx);
263                     log.debug("Added sub context: " + subctx);
264                 }
265                 catch (NameAlreadyBoundException nabe)
266                 {
267                     // ignore
268                 }
269                 catch (NamingException ne)
270                 {
271                     // even though there is a specific exception
272                     // for this condition, some implementations
273                     // throw the more general one.
274                     /*
275                       if (ne.getMessage().indexOf("already bound") == -1 )
276                       {
277                       throw ne;
278                       }
279                     */
280                     // ignore
281                 }
282                 ctx = (Context) ctx.lookup(subctx);
283             }
284             else
285             {
286                 // not really a subctx, it is the ds name
287                 ctx.bind(subctx, ds);
288             }
289         }
290     }
291 }