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 public ConnectionRepositoryEntry()
103 {
104 super();
105 }
106
107 /***
108 * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
109 */
110 public void setBeanName(String beanName)
111 {
112
113
114 if (jcdAlias == null)
115 {
116 jcdAlias = beanName;
117 }
118 }
119
120 /***
121 * @return Returns the jcdAlias.
122 */
123 public String getJcdAlias()
124 {
125 return jcdAlias;
126 }
127
128 /***
129 * @param jcdAlias The jcdAlias to set.
130 */
131 public void setJcdAlias(String jcdAlias)
132 {
133 this.jcdAlias = jcdAlias;
134 }
135
136 /***
137 * @return Returns the jndiName.
138 */
139 public String getJndiName()
140 {
141 return jndiName;
142 }
143
144 /***
145 * @param jndiName The jndiName to set.
146 */
147 public void setJndiName(String jndiName)
148 {
149 this.jndiName = jndiName;
150 }
151
152 /***
153 * @return Returns the driverClassName.
154 */
155 public String getDriverClassName()
156 {
157 return driverClassName;
158 }
159
160 /***
161 * @param driverClassName The driverClassName to set.
162 */
163 public void setDriverClassName(String driverClassName)
164 {
165 super.setDriverClassName(driverClassName);
166 this.driverClassName = driverClassName;
167 }
168
169 /***
170 * @return Returns the password.
171 */
172 public String getPassword()
173 {
174 return password;
175 }
176
177 /***
178 * @param password The password to set.
179 */
180 public void setPassword(String password)
181 {
182 super.setPassword(password);
183 this.password = password;
184 }
185
186 /***
187 * @return Returns the url.
188 */
189 public String getUrl()
190 {
191 return url;
192 }
193
194 /***
195 * @param url The url to set.
196 */
197 public void setUrl(String url)
198 {
199 super.setUrl(url);
200 this.url = url;
201 }
202
203 /***
204 * @return Returns the username.
205 */
206 public String getUsername()
207 {
208 return username;
209 }
210
211 /***
212 * @param username The username to set.
213 */
214 public void setUsername(String username)
215 {
216 super.setUsername(username);
217 this.username = username;
218 }
219
220 /***
221 * @return Returns the platform.
222 */
223 public String getPlatform()
224 {
225 return platform;
226 }
227
228 /***
229 * Set the platform attribute of the JCD. Setting this property overrides
230 * the value derived from the data source or database driver.
231 * @param platform The platform to set.
232 */
233 public void setPlatform(String platform)
234 {
235 this.platform = platform;
236 }
237
238 /***
239 * @see setJetspeedEngineScoped
240 * @return Returns if Jetspeed engine's ENC is used for JNDI lookups.
241 */
242 public boolean isJetspeedEngineScoped()
243 {
244 return jetspeedEngineScoped;
245 }
246
247 /***
248 * Sets the attribute "<code>org.apache.jetspeed.engineScoped</code>"
249 * of the JDBC connection descriptor to "<code>true</code>" or
250 * "<code>false</code>". If set, JNDI lookups of the connection will
251 * be done using the environment naming context (ENC) of the Jetspeed
252 * engine.
253 * @param jetspeedEngineScoped whether to use Jetspeed engine's ENC.
254 */
255 public void setJetspeedEngineScoped(boolean jetspeedEngineScoped)
256 {
257 this.jetspeedEngineScoped = jetspeedEngineScoped;
258 }
259
260 public void afterPropertiesSet () throws Exception
261 {
262
263 ConnectionRepository cr = MetadataManager.getInstance().connectionRepository();
264 JdbcConnectionDescriptor jcd = cr.getDescriptor(new PBKey(jcdAlias));
265 if (jcd == null)
266 {
267 jcd = new JdbcConnectionDescriptor();
268 jcd.setJcdAlias(jcdAlias);
269 cr.addDescriptor(jcd);
270 }
271 if (platform != null && platform.length() == 0)
272 {
273 platform = null;
274 }
275 DataSource ds = null;
276 JdbcMetadataUtils jdbcMetadataUtils = new JdbcMetadataUtils ();
277 if (jndiName != null)
278 {
279
280 if (connectionFactoryClass == null)
281 {
282 connectionFactoryClass = ConnectionFactoryManagedImpl.class.getName ();
283 }
284 Context initialContext = new InitialContext();
285 ds = (DataSource) initialContext.lookup(jndiName);
286 jcd.setDatasourceName(jndiName);
287 }
288 else
289 {
290
291 if (connectionFactoryClass == null)
292 {
293 connectionFactoryClass = ConnectionFactoryDBCPImpl.class.getName ();
294 }
295 jcd.setDriver(driverClassName);
296 Map conData = jdbcMetadataUtils.parseConnectionUrl(url);
297 jcd.setDbms(platform);
298 jcd.setProtocol((String)conData.get(JdbcMetadataUtils.PROPERTY_PROTOCOL));
299 jcd.setSubProtocol((String)conData.get(JdbcMetadataUtils.PROPERTY_SUBPROTOCOL));
300 jcd.setDbAlias((String)conData.get(JdbcMetadataUtils.PROPERTY_DBALIAS));
301 jcd.setUserName(username);
302 jcd.setPassWord(password);
303
304
305
306
307
308
309 ds = this;
310 }
311 ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor();
312 if (cpd == null)
313 {
314 cpd = new ConnectionPoolDescriptor();
315 jcd.setConnectionPoolDescriptor(cpd);
316 }
317 Class conFacCls = ClassHelper.getClass(connectionFactoryClass);
318 cpd.setConnectionFactory(conFacCls);
319
320 jdbcMetadataUtils.fillJCDFromDataSource(jcd, ds, null, null);
321
322 if (platform == null && JdbcMetadataUtils.PLATFORM_ORACLE.equals(jcd.getDbms())) {
323
324 updateOraclePlatform (jcd, ds);
325 }
326
327 if (platform != null) {
328 if (!platform.equals(jcd.getDbms())) {
329 log.warn ("Automatically derived RDBMS platform \"" + jcd.getDbms()
330 + "\" differs from explicitly set platform \"" + platform + "\"");
331 }
332 jcd.setDbms(platform);
333 } else {
334 platform = jcd.getDbms();
335 }
336
337
338 jcd.addAttribute("org.apache.jetspeed.engineScoped",
339 Boolean.toString(jetspeedEngineScoped));
340 }
341
342 /***
343 * @param jcd
344 * @throws LookupException
345 * @throws IllegalAccessException
346 * @throws InstantiationException
347 * throws SQLException
348 */
349 private void updateOraclePlatform(JdbcConnectionDescriptor jcd, DataSource ds)
350 throws LookupException, IllegalAccessException, InstantiationException, SQLException
351 {
352 Connection con = null;
353 try
354 {
355 con = ds.getConnection();
356 DatabaseMetaData metaData = con.getMetaData();
357 int rdbmsVersion = 0;
358 try
359 {
360
361
362 rdbmsVersion = metaData.getDatabaseMajorVersion();
363 } catch (Throwable t) {
364 String dbVersion = metaData.getDatabaseProductVersion();
365 String relKey = "Release";
366 String major = dbVersion;
367 int startPos = dbVersion.indexOf(relKey);
368 if (startPos < 0)
369 {
370 log.warn ("Cannot determine Oracle version, no \"Release\" in procuct version: \"" + dbVersion + "\"");
371 return;
372 }
373 startPos += relKey.length();
374 int dotPos = dbVersion.indexOf('.', startPos);
375 if (dotPos > 0) {
376 major = dbVersion.substring(startPos, dotPos).trim();
377 }
378 try
379 {
380 rdbmsVersion = Integer.parseInt(major);
381 }
382 catch (NumberFormatException e)
383 {
384 log.warn ("Cannot determine Oracle version, product version \"" + dbVersion + "\" not layed out as \"... Release N.M.....\"");
385 return;
386 }
387 if (log.isDebugEnabled())
388 {
389 log.debug ("Extracted Oracle major version " + rdbmsVersion + " from product version \"" + dbVersion + "\"");
390 }
391 }
392 if (rdbmsVersion >= 9) {
393 jcd.setDbms(JdbcMetadataUtils.PLATFORM_ORACLE9I);
394 }
395 }
396 finally
397 {
398 if (con != null) {
399 con.close ();
400 }
401 }
402 }
403
404 /***
405 * a minimal DataSource implementation that satisfies the requirements
406 * of JdbcMetadataUtil.
407 */
408 private class MinimalDataSource implements DataSource
409 {
410 private JdbcConnectionDescriptor jcd = null;
411
412 /***
413 * Create a new instance using the given JCD.
414 */
415 public MinimalDataSource (JdbcConnectionDescriptor jcd)
416 {
417 this.jcd = jcd;
418 }
419
420
421
422
423 public Connection getConnection() throws SQLException {
424
425
426 try {
427
428 ClassHelper.getClass(jcd.getDriver(), true);
429 String url = jcd.getProtocol() + ":" + jcd.getSubProtocol() + ":" + jcd.getDbAlias();
430 if (jcd.getUserName() == null)
431 {
432 return DriverManager.getConnection(url);
433 }
434 else
435 {
436 return DriverManager.getConnection(url, jcd.getUserName(), jcd.getPassWord());
437 }
438 }
439 catch (ClassNotFoundException e)
440 {
441 throw (IllegalStateException)
442 (new IllegalStateException (e.getMessage ())).initCause (e);
443 }
444 }
445
446 /***
447 * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
448 */
449 public Connection getConnection(String username, String password)
450 throws SQLException {
451 return getConnection ();
452 }
453
454 /***
455 * @see javax.sql.DataSource#getLoginTimeout()
456 */
457 public int getLoginTimeout() throws SQLException
458 {
459 return 0;
460 }
461
462 /***
463 * @see javax.sql.DataSource#getLogWriter()
464 */
465 public PrintWriter getLogWriter() throws SQLException {
466 return null;
467 }
468
469 /***
470 * @see javax.sql.DataSource#setLoginTimeout(int)
471 */
472 public void setLoginTimeout(int seconds) throws SQLException {
473 }
474
475 /***
476 * @see javax.sql.DataSource#setLogWriter(java.io.PrintWriter)
477 */
478 public void setLogWriter(PrintWriter out) throws SQLException {
479 }
480 }
481
482 }