1 package org.apache.torque.dsfactory;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 239636 2005-08-24 12:38:09Z henning $
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;
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 super.initialize(configuration);
100
101 initJNDI(configuration);
102 initDataSource(configuration);
103 }
104
105 /***
106 * Initializes JNDI.
107 *
108 * @param configuration where to read the settings from
109 * @throws TorqueException if a property set fails
110 */
111 private void initJNDI(Configuration configuration) throws TorqueException
112 {
113 log.debug("Starting initJNDI");
114
115 Configuration c = configuration.subset("jndi");
116 if (c == null || c.isEmpty())
117 {
118 throw new TorqueException(
119 "JndiDataSourceFactory requires a jndi "
120 + "path property to lookup the DataSource in JNDI.");
121 }
122
123 try
124 {
125 Hashtable env = new Hashtable();
126 for (Iterator i = c.getKeys(); i.hasNext(); )
127 {
128 String key = (String) i.next();
129 if (key.equals("path"))
130 {
131 path = c.getString(key);
132 if (log.isDebugEnabled())
133 {
134 log.debug("JNDI path: " + path);
135 }
136 }
137 else if (key.equals("ttl"))
138 {
139 ttl = c.getLong(key, ttl);
140 if (log.isDebugEnabled())
141 {
142 log.debug("Time between context lookups: " + ttl);
143 }
144 }
145 else
146 {
147 String value = c.getString(key);
148 env.put(key, value);
149 if (log.isDebugEnabled())
150 {
151 log.debug("Set jndi property: " + key + "=" + value);
152 }
153 }
154 }
155
156 ctx = new InitialContext(env);
157 log.debug("Created new InitialContext");
158 debugCtx(ctx);
159 }
160 catch (Exception e)
161 {
162 log.error("", e);
163 throw new TorqueException(e);
164 }
165 }
166
167 /***
168 * Initializes the DataSource.
169 *
170 * @param configuration where to read the settings from
171 * @throws TorqueException if a property set fails
172 */
173 private void initDataSource(Configuration configuration)
174 throws TorqueException
175 {
176 log.debug("Starting initDataSource");
177 try
178 {
179 Object ds = null;
180
181 Configuration c = configuration.subset("datasource");
182 if (c != null)
183 {
184 for (Iterator i = c.getKeys(); i.hasNext(); )
185 {
186 String key = (String) i.next();
187 if (key.equals("classname"))
188 {
189 String classname = c.getString(key);
190 if (log.isDebugEnabled())
191 {
192 log.debug("Datasource class: " + classname);
193 }
194
195 Class dsClass = Class.forName(classname);
196 ds = dsClass.newInstance();
197 }
198 else
199 {
200 if (ds != null)
201 {
202 if (log.isDebugEnabled())
203 {
204 log.debug("Setting datasource property: " + key);
205 }
206 setProperty(key, c, ds);
207 }
208 else
209 {
210 log.error("Tried to set property " + key + " without Datasource definition!");
211 }
212 }
213 }
214 }
215
216 if (ds != null)
217 {
218 bindDStoJndi(ctx, path, ds);
219 }
220 }
221 catch (Exception e)
222 {
223 log.error("", e);
224 throw new TorqueException(e);
225 }
226 }
227
228 /***
229 * Does nothing. We do not want to close a dataSource retrieved from Jndi,
230 * because other applications might use it as well.
231 */
232 public void close()
233 {
234
235 }
236
237 /***
238 *
239 * @param ctx the context
240 * @throws NamingException
241 */
242 private void debugCtx(Context ctx) throws NamingException
243 {
244 log.debug("InitialContext -------------------------------");
245 Map env = ctx.getEnvironment();
246 Iterator qw = env.keySet().iterator();
247 log.debug("Environment properties:" + env.size());
248 while (qw.hasNext())
249 {
250 Object prop = qw.next();
251 log.debug(" " + prop + ": " + env.get(prop));
252 }
253 log.debug("----------------------------------------------");
254 }
255
256 /***
257 *
258 * @param ctx
259 * @param path
260 * @param ds
261 * @throws Exception
262 */
263 private void bindDStoJndi(Context ctx, String path, Object ds)
264 throws Exception
265 {
266 debugCtx(ctx);
267
268
269 int start = path.indexOf(':') + 1;
270 if (start > 0)
271 {
272 path = path.substring(start);
273 }
274 StringTokenizer st = new StringTokenizer(path, "/");
275 while (st.hasMoreTokens())
276 {
277 String subctx = st.nextToken();
278 if (st.hasMoreTokens())
279 {
280 try
281 {
282 ctx.createSubcontext(subctx);
283 log.debug("Added sub context: " + subctx);
284 }
285 catch (NameAlreadyBoundException nabe)
286 {
287
288 }
289 catch (NamingException ne)
290 {
291
292
293
294
295
296
297
298
299
300
301 }
302 ctx = (Context) ctx.lookup(subctx);
303 }
304 else
305 {
306
307 ctx.bind(subctx, ds);
308 }
309 }
310 }
311 }