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,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;
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
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
268 }
269 catch (NamingException ne)
270 {
271
272
273
274
275
276
277
278
279
280
281 }
282 ctx = (Context) ctx.lookup(subctx);
283 }
284 else
285 {
286
287 ctx.bind(subctx, ds);
288 }
289 }
290 }
291 }