1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.components.rdbms.ojb;
18
19 import java.io.PrintWriter;
20 import java.sql.Connection;
21 import java.sql.DatabaseMetaData;
22 import java.sql.DriverManager;
23 import java.sql.SQLException;
24 import java.util.Map;
25
26 import javax.naming.Context;
27 import javax.naming.InitialContext;
28 import javax.sql.DataSource;
29
30 import org.apache.commons.dbcp.BasicDataSource;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.ojb.broker.PBKey;
34 import org.apache.ojb.broker.accesslayer.ConnectionFactoryDBCPImpl;
35 import org.apache.ojb.broker.accesslayer.ConnectionFactoryManagedImpl;
36 import org.apache.ojb.broker.accesslayer.LookupException;
37 import org.apache.ojb.broker.metadata.ConnectionPoolDescriptor;
38 import org.apache.ojb.broker.metadata.ConnectionRepository;
39 import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
40 import org.apache.ojb.broker.metadata.JdbcMetadataUtils;
41 import org.apache.ojb.broker.metadata.MetadataManager;
42 import org.apache.ojb.broker.util.ClassHelper;
43 import org.springframework.beans.factory.BeanNameAware;
44 import org.springframework.beans.factory.InitializingBean;
45
46 /***
47 * A JavaBean that configures an entry in OJB's ConnectionRepository
48 * according to its properties. If a JCD alias is not specified, it defaults
49 * to the bean's name in the Spring configuration. If the JDBC connection
50 * descriptor already exists (e.g. because it has been defined in OJB's
51 * configuration) the properties are merged into the existing descriptor
52 * (see note about "platform" below), else the JDBC connection descriptor
53 * is created.<P>
54 *
55 * If a JNDI name is set, the bean automatically configures a JDBC connection
56 * descriptor with a connection factory of type
57 * <code>ConnectionFactoryManagedImpl</code>, else it uses
58 * <code>ConectionFactoryDBCPImpl</code>. This may be overridden my setting
59 * the connection factory property explicitly.<P>
60 *
61 * Properties "driverClassName", "url", "username" and "password" are used
62 * only if no JNDI name is set, i.e. if the connection factory uses the
63 * driver to create data sources.<P>
64 *
65 * The bean derives the RDBMS platform setting from the configured
66 * data source or database driver using OJB's <code>JdbcMetadataUtils</code>
67 * class. At least until OJB 1.0.3, however, this class does not properly
68 * distinguish the platforms "Oracle" and "Oracle9i"; it always assigns
69 * "Oracle". In case of "Oracle", this bean therefore opens a connection,
70 * obtains the version information from the database server and adjusts the
71 * platform accordingly. This behaviour may be overridden by setting the
72 * <code>platform</code> property of the bean explicitly. Note that the
73 * attribute "platform" of an already existing JCD is ignored. An already
74 * existing JCD stems most likely from a configuration file "repository.xml".
75 * As the DTD for "repository.xml" ("repository.dtd") defines a default
76 * value for attribute "platform" ("Hsqldb"), it is in general impossible
77 * to find out whether the platform attribute of an existing JCD has been set
78 * explicitly or has simply assumed its default value.
79 *
80 * @author Michael Lipp
81 * @version $Id$
82 */
83 public class ConnectionRepositoryEntry
84 extends BasicDataSource
85 implements BeanNameAware, InitializingBean
86 {
87 private static final Log log = LogFactory.getLog(ConnectionRepositoryEntry.class);
88
89
90 private String jcdAlias = null;
91 private String platform = null;
92 private String connectionFactoryClass = null;
93
94 private String jndiName = null;
95
96 private String driverClassName = null;
97 private String url = null;
98 private String username = null;
99 private String password = null;
100 private boolean jetspeedEngineScoped = true;
101
102 private DataSource externalDs;
103
104 public ConnectionRepositoryEntry()
105 {
106 super();
107 }
108
109 /***
110 * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
111 */
112 public void setBeanName(String beanName)
113 {
114
115
116 if (jcdAlias == null)
117 {
118 jcdAlias = beanName;
119 }
120 }
121
122 /***
123 * @return Returns the jcdAlias.
124 */
125 public String getJcdAlias()
126 {
127 return jcdAlias;
128 }
129
130 /***
131 * @param jcdAlias The jcdAlias to set.
132 */
133 public void setJcdAlias(String jcdAlias)
134 {
135 this.jcdAlias = jcdAlias;
136 }
137
138 /***
139 * @return Returns the jndiName.
140 */
141 public String getJndiName()
142 {
143 return jndiName;
144 }
145
146 /***
147 * @param jndiName The jndiName to set.
148 */
149 public void setJndiName(String jndiName)
150 {
151 this.jndiName = jndiName;
152 }
153
154 /***
155 * @return Returns the driverClassName.
156 */
157 public String getDriverClassName()
158 {
159 return driverClassName;
160 }
161
162 /***
163 * @param driverClassName The driverClassName to set.
164 */
165 public void setDriverClassName(String driverClassName)
166 {
167 super.setDriverClassName(driverClassName);
168 this.driverClassName = driverClassName;
169 }
170
171 /***
172 * @return Returns the password.
173 */
174 public String getPassword()
175 {
176 return password;
177 }
178
179 /***
180 * @param password The password to set.
181 */
182 public void setPassword(String password)
183 {
184 super.setPassword(password);
185 this.password = password;
186 }
187
188 /***
189 * @return Returns the url.
190 */
191 public String getUrl()
192 {
193 return url;
194 }
195
196 /***
197 * @param url The url to set.
198 */
199 public void setUrl(String url)
200 {
201 super.setUrl(url);
202 this.url = url;
203 }
204
205 /***
206 * @return Returns the username.
207 */
208 public String getUsername()
209 {
210 return username;
211 }
212
213 /***
214 * @param username The username to set.
215 */
216 public void setUsername(String username)
217 {
218 super.setUsername(username);
219 this.username = username;
220 }
221
222 /***
223 * @return Returns the platform.
224 */
225 public String getPlatform()
226 {
227 return platform;
228 }
229
230 /***
231 * Set the platform attribute of the JCD. Setting this property overrides
232 * the value derived from the data source or database driver.
233 * @param platform The platform to set.
234 */
235 public void setPlatform(String platform)
236 {
237 this.platform = platform;
238 }
239
240 /***
241 * @see setJetspeedEngineScoped
242 * @return Returns if Jetspeed engine's ENC is used for JNDI lookups.
243 */
244 public boolean isJetspeedEngineScoped()
245 {
246 return jetspeedEngineScoped;
247 }
248
249 /***
250 * Sets the attribute "<code>org.apache.jetspeed.engineScoped</code>"
251 * of the JDBC connection descriptor to "<code>true</code>" or
252 * "<code>false</code>". If set, JNDI lookups of the connection will
253 * be done using the environment naming context (ENC) of the Jetspeed
254 * engine.
255 * @param jetspeedEngineScoped whether to use Jetspeed engine's ENC.
256 */
257 public void setJetspeedEngineScoped(boolean jetspeedEngineScoped)
258 {
259 this.jetspeedEngineScoped = jetspeedEngineScoped;
260 }
261
262 public void afterPropertiesSet () throws Exception
263 {
264
265 ConnectionRepository cr = MetadataManager.getInstance().connectionRepository();
266 JdbcConnectionDescriptor jcd = cr.getDescriptor(new PBKey(jcdAlias));
267 if (jcd == null)
268 {
269 jcd = new JdbcConnectionDescriptor();
270 jcd.setJcdAlias(jcdAlias);
271 cr.addDescriptor(jcd);
272 }
273 if (platform != null && platform.length() == 0)
274 {
275 platform = null;
276 }
277 DataSource ds = null;
278 JdbcMetadataUtils jdbcMetadataUtils = new JdbcMetadataUtils ();
279 if (jndiName != null)
280 {
281
282 if (connectionFactoryClass == null)
283 {
284 connectionFactoryClass = ConnectionFactoryManagedImpl.class.getName ();
285 }
286 Context initialContext = new InitialContext();
287 ds = (DataSource) initialContext.lookup(jndiName);
288 externalDs = ds;
289 jcd.setDatasourceName(jndiName);
290 }
291 else
292 {
293
294 if (connectionFactoryClass == null)
295 {
296 connectionFactoryClass = ConnectionFactoryDBCPImpl.class.getName ();
297 }
298 jcd.setDriver(driverClassName);
299 Map conData = jdbcMetadataUtils.parseConnectionUrl(url);
300 jcd.setDbms(platform);
301 jcd.setProtocol((String)conData.get(JdbcMetadataUtils.PROPERTY_PROTOCOL));
302 jcd.setSubProtocol((String)conData.get(JdbcMetadataUtils.PROPERTY_SUBPROTOCOL));
303 jcd.setDbAlias((String)conData.get(JdbcMetadataUtils.PROPERTY_DBALIAS));
304 jcd.setUserName(username);
305 jcd.setPassWord(password);
306
307
308
309
310
311
312 ds = this;
313 }
314 ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor();
315 if (cpd == null)
316 {
317 cpd = new ConnectionPoolDescriptor();
318 jcd.setConnectionPoolDescriptor(cpd);
319 }
320 Class conFacCls = ClassHelper.getClass(connectionFactoryClass);
321 cpd.setConnectionFactory(conFacCls);
322
323 jdbcMetadataUtils.fillJCDFromDataSource(jcd, ds, null, null);
324
325 if (platform == null && JdbcMetadataUtils.PLATFORM_ORACLE.equals(jcd.getDbms())) {
326
327 updateOraclePlatform (jcd, ds);
328 }
329
330 if (platform != null) {
331 if (!platform.equals(jcd.getDbms())) {
332 log.warn ("Automatically derived RDBMS platform \"" + jcd.getDbms()
333 + "\" differs from explicitly set platform \"" + platform + "\"");
334 }
335 jcd.setDbms(platform);
336 } else {
337 platform = jcd.getDbms();
338 }
339
340
341 jcd.addAttribute("org.apache.jetspeed.engineScoped",
342 Boolean.toString(jetspeedEngineScoped));
343 }
344
345 /***
346 * @param jcd
347 * @throws LookupException
348 * @throws IllegalAccessException
349 * @throws InstantiationException
350 * throws SQLException
351 */
352 private void updateOraclePlatform(JdbcConnectionDescriptor jcd, DataSource ds)
353 throws LookupException, IllegalAccessException, InstantiationException, SQLException
354 {
355 Connection con = null;
356 try
357 {
358 con = ds.getConnection();
359 DatabaseMetaData metaData = con.getMetaData();
360 int rdbmsVersion = 0;
361 try
362 {
363
364
365 rdbmsVersion = metaData.getDatabaseMajorVersion();
366 } catch (Throwable t) {
367 String dbVersion = metaData.getDatabaseProductVersion();
368 String relKey = "Release";
369 String major = dbVersion;
370 int startPos = dbVersion.indexOf(relKey);
371 if (startPos < 0)
372 {
373 log.warn ("Cannot determine Oracle version, no \"Release\" in procuct version: \"" + dbVersion + "\"");
374 return;
375 }
376 startPos += relKey.length();
377 int dotPos = dbVersion.indexOf('.', startPos);
378 if (dotPos > 0) {
379 major = dbVersion.substring(startPos, dotPos).trim();
380 }
381 try
382 {
383 rdbmsVersion = Integer.parseInt(major);
384 }
385 catch (NumberFormatException e)
386 {
387 log.warn ("Cannot determine Oracle version, product version \"" + dbVersion + "\" not layed out as \"... Release N.M.....\"");
388 return;
389 }
390 if (log.isDebugEnabled())
391 {
392 log.debug ("Extracted Oracle major version " + rdbmsVersion + " from product version \"" + dbVersion + "\"");
393 }
394 }
395 if (rdbmsVersion >= 9) {
396 jcd.setDbms(JdbcMetadataUtils.PLATFORM_ORACLE9I);
397 }
398 }
399 finally
400 {
401 if (con != null) {
402 con.close ();
403 }
404 }
405 }
406
407 /***
408 * a minimal DataSource implementation that satisfies the requirements
409 * of JdbcMetadataUtil.
410 */
411 private class MinimalDataSource implements DataSource
412 {
413 private JdbcConnectionDescriptor jcd = null;
414
415 /***
416 * Create a new instance using the given JCD.
417 */
418 public MinimalDataSource (JdbcConnectionDescriptor jcd)
419 {
420 this.jcd = jcd;
421 }
422
423
424
425
426 public Connection getConnection() throws SQLException {
427
428
429 try {
430
431 ClassHelper.getClass(jcd.getDriver(), true);
432 String url = jcd.getProtocol() + ":" + jcd.getSubProtocol() + ":" + jcd.getDbAlias();
433 if (jcd.getUserName() == null)
434 {
435 return DriverManager.getConnection(url);
436 }
437 else
438 {
439 return DriverManager.getConnection(url, jcd.getUserName(), jcd.getPassWord());
440 }
441 }
442 catch (ClassNotFoundException e)
443 {
444 throw (IllegalStateException)
445 (new IllegalStateException (e.getMessage ())).initCause (e);
446 }
447 }
448
449 /***
450 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
451 */
452 public Connection getConnection(String username, String password)
453 throws SQLException {
454 return getConnection ();
455 }
456
457 /***
458 * @see javax.sql.DataSource#getLoginTimeout()
459 */
460 public int getLoginTimeout() throws SQLException
461 {
462 return 0;
463 }
464
465 /***
466 * @see javax.sql.DataSource#getLogWriter()
467 */
468 public PrintWriter getLogWriter() throws SQLException {
469 return null;
470 }
471
472 /***
473 * @see javax.sql.DataSource#setLoginTimeout(int)
474 */
475 public void setLoginTimeout(int seconds) throws SQLException {
476 }
477
478 /***
479 * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
480 */
481 public void setLogWriter(PrintWriter out) throws SQLException {
482 }
483 }
484
485 public Connection getConnection() throws SQLException {
486 if(externalDs != null)
487 {
488 return externalDs.getConnection();
489 }
490 else
491 {
492 return super.getConnection();
493 }
494 }
495
496 public Connection getConnection(String username, String password)
497 throws SQLException {
498
499 if(externalDs != null)
500 {
501 return externalDs.getConnection(username, password);
502 }
503 else
504 {
505 return super.getConnection(username, password);
506 }
507 }
508
509 }