001: package org.apache.ojb.broker.metadata;
002:
003: /* Copyright 2002-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.sql.Connection;
019: import java.sql.DatabaseMetaData;
020: import java.sql.SQLException;
021: import java.util.HashMap;
022:
023: import javax.sql.DataSource;
024: import org.apache.commons.beanutils.PropertyUtils;
025:
026: /**
027: * This class provides some utility functions to OJB for working with JDBC metadata.
028: *
029: * @author <a href="mailto:tomdz@apache.org">Thomas Dudziak</a>
030: */
031: public class JdbcMetadataUtils {
032: /** The name of the property returned by the {@link #splitConnectionUrl(String)} method
033: that contains the protocol */
034: public static final String PROPERTY_PROTOCOL = "protocol";
035: /** The name of the property returned by the {@link #splitConnectionUrl(String)} method
036: that contains the sub protocol */
037: public static final String PROPERTY_SUBPROTOCOL = "subprotocol";
038: /** The name of the property returned by the {@link #splitConnectionUrl(String)} method
039: that contains the database alias (the actual database url) */
040: public static final String PROPERTY_DBALIAS = "dbAlias";
041:
042: /** Identifier for the DB2 platform */
043: public static final String PLATFORM_DB2 = "Db2";
044: /** Identifier for the Firebird platform */
045: public static final String PLATFORM_FIREBIRD = "Firebird";
046: /** Identifier for the Hsqldb platform */
047: public static final String PLATFORM_HSQLDB = "Hsqldb";
048: /** Identifier for the Informix platform */
049: public static final String PLATFORM_INFORMIX = "Informix";
050: /** Identifier for the MaxDB platform */
051: public static final String PLATFORM_MAXDB = "MaxDB";
052: /** Identifier for the McKoi platform */
053: public static final String PLATFORM_MCKOI = "McKoi";
054: /** Identifier for the MsAccess platform */
055: public static final String PLATFORM_MSACCESS = "MsAccess";
056: /** Identifier for the Microsoft SQL Server platform */
057: public static final String PLATFORM_MSSQLSERVER = "MsSQLServer";
058: /** Identifier for the MySQL platform */
059: public static final String PLATFORM_MYSQL = "MySQL";
060: /** Identifier for the generic Oracle platform */
061: public static final String PLATFORM_ORACLE = "Oracle";
062: /** Identifier for the Oracle9i platform */
063: public static final String PLATFORM_ORACLE9I = "Oracle9i";
064: /** Identifier for the PostgresSQL platform */
065: public static final String PLATFORM_POSTGRESQL = "PostgreSQL";
066: /** Identifier for the generic Sybase platform */
067: public static final String PLATFORM_SYBASE = "Sybase";
068: /** Identifier for the Sybase ASA platform */
069: public static final String PLATFORM_SYBASEASA = "SybaseASA";
070: /** Identifier for the Sybase ASE platform */
071: public static final String PLATFORM_SYBASEASE = "SybaseASE";
072: /** Identifier for the Oracle9i for WebLogic platform */
073: public static final String PLATFORM_WLORACLE9I = "WLOracle9i";
074:
075: /** The standard DB2 jdbc driver */
076: public static final String DRIVER_DB2 = "COM.ibm.db2.jdbc.app.DB2Driver";
077: /** The i-net DB2 jdbc driver */
078: public static final String DRIVER_DB2_INET = "com.inet.drda.DRDADriver";
079: /** The standard Firebird jdbc driver */
080: public static final String DRIVER_FIREBIRD = "org.firebirdsql.jdbc.FBDriver";
081: /** The standard Hsqldb jdbc driver */
082: public static final String DRIVER_HSQLDB = "org.hsqldb.jdbcDriver";
083: /** The i-net pooled jdbc driver for SQLServer and Sybase */
084: public static final String DRIVER_INET_POOLED = "com.inet.pool.PoolDriver";
085: /** The standard Informix jdbc driver */
086: public static final String DRIVER_INFORMIX = "com.informix.jdbc.IfxDriver";
087: /** The jTDS jdbc driver for SQLServer and Sybase */
088: public static final String DRIVER_JTDS = "net.sourceforge.jtds.jdbc.Driver";
089: /** The standard MaxDB jdbc driver */
090: public static final String DRIVER_MAXDB = "com.sap.dbtech.jdbc.DriverSapDB";
091: /** The standard McKoi jdbc driver */
092: public static final String DRIVER_MCKOI = "com.mckoi.JDBCDriver";
093: /** The standard SQLServer jdbc driver */
094: public static final String DRIVER_MSSQLSERVER = "com.microsoft.jdbc.sqlserver.SQLServerDriver";
095: /** The i-net SQLServer jdbc driver */
096: public static final String DRIVER_MSSQLSERVER_INET = "com.inet.tds.TdsDriver";
097: /** The JNetDirect SQLServer jdbc driver */
098: public static final String DRIVER_MSSQLSERVER_JSQLCONNECT = "com.jnetdirect.jsql.JSQLDriver";
099: /** The standard MySQL jdbc driver */
100: public static final String DRIVER_MYSQL = "com.mysql.jdbc.Driver";
101: /** The old MySQL jdbc driver */
102: public static final String DRIVER_MYSQL_OLD = "org.gjt.mm.mysql.Driver";
103: /** The standard Oracle jdbc driver */
104: public static final String DRIVER_ORACLE = "oracle.jdbc.driver.OracleDriver";
105: /** The i-net Oracle jdbc driver */
106: public static final String DRIVER_ORACLE_INET = "com.inet.ora.OraDriver";
107: /** The standard PostgreSQL jdbc driver */
108: public static final String DRIVER_POSTGRESQL = "org.postgresql.Driver";
109: /** The standard Sapdb jdbc driver */
110: public static final String DRIVER_SAPDB = DRIVER_MAXDB;
111: /** The standard Sybase jdbc driver */
112: public static final String DRIVER_SYBASE = "com.sybase.jdbc2.jdbc.SybDriver";
113: /** The old Sybase jdbc driver */
114: public static final String DRIVER_SYBASE_OLD = "com.sybase.jdbc.SybDriver";
115: /** The i-net Sybase jdbc driver */
116: public static final String DRIVER_SYBASE_INET = "com.inet.syb.SybDriver";
117:
118: /** The subprotocol used by the standard DB2 driver */
119: public static final String SUBPROTOCOL_DB2 = "db2";
120: /** The subprotocol used by the i-net DB2 driver */
121: public static final String SUBPROTOCOL_DB2_INET = "inetdb2";
122: /** The subprotocol used by the standard Firebird driver */
123: public static final String SUBPROTOCOL_FIREBIRD = "firebirdsql";
124: /** The subprotocol used by the standard Hsqldb driver */
125: public static final String SUBPROTOCOL_HSQLDB = "hsqldb";
126: /** The subprotocol used by the standard Informix driver */
127: public static final String SUBPROTOCOL_INFORMIX = "informix-sqli";
128: /** The subprotocol used by the standard MaxDB driver */
129: public static final String SUBPROTOCOL_MAXDB = "sapdb";
130: /** The subprotocol used by the standard McKoi driver */
131: public static final String SUBPROTOCOL_MCKOI = "mckoi";
132: /** The subprotocol used by the standard SQLServer driver */
133: public static final String SUBPROTOCOL_MSSQLSERVER = "microsoft:sqlserver";
134: /** A subprotocol used by the i-net SQLServer driver */
135: public static final String SUBPROTOCOL_MSSQLSERVER_INET = "inetdae";
136: /** A subprotocol used by the i-net SQLServer driver */
137: public static final String SUBPROTOCOL_MSSQLSERVER6_INET = "inetdae6";
138: /** A subprotocol used by the i-net SQLServer driver */
139: public static final String SUBPROTOCOL_MSSQLSERVER7_INET = "inetdae7";
140: /** A subprotocol used by the i-net SQLServer driver */
141: public static final String SUBPROTOCOL_MSSQLSERVER7A_INET = "inetdae7a";
142: /** A subprotocol used by the pooled i-net SQLServer driver */
143: public static final String SUBPROTOCOL_MSSQLSERVER_INET_POOLED = "inetpool:inetdae";
144: /** A subprotocol used by the pooled i-net SQLServer driver */
145: public static final String SUBPROTOCOL_MSSQLSERVER6_INET_POOLED = "inetpool:inetdae6";
146: /** A subprotocol used by the pooled i-net SQLServer driver */
147: public static final String SUBPROTOCOL_MSSQLSERVER7_INET_POOLED = "inetpool:inetdae7";
148: /** A subprotocol used by the pooled i-net SQLServer driver */
149: public static final String SUBPROTOCOL_MSSQLSERVER7A_INET_POOLED = "inetpool:inetdae7a";
150: /** The subprotocol used by the JNetDirect SQLServer driver */
151: public static final String SUBPROTOCOL_MSSQLSERVER_JSQLCONNECT = "JSQLConnect";
152: /** The subprotocol used by the jTDS SQLServer driver */
153: public static final String SUBPROTOCOL_MSSQLSERVER_JTDS = "jtds:sqlserver";
154: /** The subprotocol used by the standard MySQL driver */
155: public static final String SUBPROTOCOL_MYSQL = "mysql";
156: /** The subprotocol used by the standard Oracle driver */
157: public static final String SUBPROTOCOL_ORACLE = "oracle";
158: /** The subprotocol used by the i-net Oracle driver */
159: public static final String SUBPROTOCOL_ORACLE_INET = "inetora";
160: /** The subprotocol used by the standard PostgreSQL driver */
161: public static final String SUBPROTOCOL_POSTGRESQL = "postgresql";
162: /** The subprotocol used by the standard Sapdb driver */
163: public static final String SUBPROTOCOL_SAPDB = SUBPROTOCOL_MAXDB;
164: /** The subprotocol used by the standard Sybase driver */
165: public static final String SUBPROTOCOL_SYBASE = "sybase:Tds";
166: /** The subprotocol used by the i-net Sybase driver */
167: public static final String SUBPROTOCOL_SYBASE_INET = "inetsyb";
168: /** The subprotocol used by the pooled i-net Sybase driver */
169: public static final String SUBPROTOCOL_SYBASE_INET_POOLED = "inetpool:inetsyb";
170: /** The subprotocol used by the jTDS Sybase driver */
171: public static final String SUBPROTOCOL_SYBASE_JTDS = "jtds:sybase";
172:
173: /** Maps the sub-protocl part of a jdbc connection url to a OJB platform name */
174: private HashMap jdbcSubProtocolToPlatform = new HashMap();
175: /** Maps the jdbc driver name to a OJB platform name */
176: private HashMap jdbcDriverToPlatform = new HashMap();
177:
178: /**
179: * Creates a new <code>JdbcMetadataUtils</code> object.
180: */
181: public JdbcMetadataUtils() {
182: // Note that currently Sapdb and MaxDB have equal subprotocols and
183: // drivers so we have no means to distinguish them
184: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_DB2, PLATFORM_DB2);
185: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_DB2_INET,
186: PLATFORM_DB2);
187: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_FIREBIRD,
188: PLATFORM_FIREBIRD);
189: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_HSQLDB,
190: PLATFORM_HSQLDB);
191: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_INFORMIX,
192: PLATFORM_INFORMIX);
193: jdbcSubProtocolToPlatform
194: .put(SUBPROTOCOL_MAXDB, PLATFORM_MAXDB);
195: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_MSSQLSERVER,
196: PLATFORM_MSSQLSERVER);
197: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_MSSQLSERVER_INET,
198: PLATFORM_MSSQLSERVER);
199: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_MSSQLSERVER6_INET,
200: PLATFORM_MSSQLSERVER);
201: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_MSSQLSERVER7_INET,
202: PLATFORM_MSSQLSERVER);
203: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_MSSQLSERVER7A_INET,
204: PLATFORM_MSSQLSERVER);
205: jdbcSubProtocolToPlatform.put(
206: SUBPROTOCOL_MSSQLSERVER_INET_POOLED,
207: PLATFORM_MSSQLSERVER);
208: jdbcSubProtocolToPlatform.put(
209: SUBPROTOCOL_MSSQLSERVER6_INET_POOLED,
210: PLATFORM_MSSQLSERVER);
211: jdbcSubProtocolToPlatform.put(
212: SUBPROTOCOL_MSSQLSERVER7_INET_POOLED,
213: PLATFORM_MSSQLSERVER);
214: jdbcSubProtocolToPlatform.put(
215: SUBPROTOCOL_MSSQLSERVER7A_INET_POOLED,
216: PLATFORM_MSSQLSERVER);
217: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_MSSQLSERVER_JTDS,
218: PLATFORM_MSSQLSERVER);
219: jdbcSubProtocolToPlatform
220: .put(SUBPROTOCOL_MYSQL, PLATFORM_MYSQL);
221: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_ORACLE,
222: PLATFORM_ORACLE);
223: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_ORACLE_INET,
224: PLATFORM_ORACLE);
225: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_POSTGRESQL,
226: PLATFORM_POSTGRESQL);
227: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_SYBASE,
228: PLATFORM_SYBASE);
229: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_SYBASE_INET,
230: PLATFORM_SYBASE);
231: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_SYBASE_INET_POOLED,
232: PLATFORM_SYBASE);
233: jdbcSubProtocolToPlatform.put(SUBPROTOCOL_SYBASE_JTDS,
234: PLATFORM_SYBASE);
235:
236: jdbcDriverToPlatform.put(DRIVER_DB2, PLATFORM_DB2);
237: jdbcDriverToPlatform.put(DRIVER_DB2_INET, PLATFORM_DB2);
238: jdbcDriverToPlatform.put(DRIVER_FIREBIRD, PLATFORM_FIREBIRD);
239: jdbcDriverToPlatform.put(DRIVER_HSQLDB, PLATFORM_HSQLDB);
240: jdbcDriverToPlatform.put(DRIVER_INFORMIX, PLATFORM_INFORMIX);
241: jdbcDriverToPlatform.put(DRIVER_MAXDB, PLATFORM_MAXDB);
242: jdbcDriverToPlatform.put(DRIVER_MCKOI, PLATFORM_MCKOI);
243: jdbcDriverToPlatform.put(DRIVER_MSSQLSERVER,
244: PLATFORM_MSSQLSERVER);
245: jdbcDriverToPlatform.put(DRIVER_MSSQLSERVER_INET,
246: PLATFORM_MSSQLSERVER);
247: jdbcDriverToPlatform.put(DRIVER_MSSQLSERVER_JSQLCONNECT,
248: PLATFORM_MSSQLSERVER);
249: jdbcDriverToPlatform.put(DRIVER_MYSQL, PLATFORM_MYSQL);
250: jdbcDriverToPlatform.put(DRIVER_MYSQL_OLD, PLATFORM_MYSQL);
251: jdbcDriverToPlatform.put(DRIVER_ORACLE, PLATFORM_ORACLE);
252: jdbcDriverToPlatform.put(DRIVER_ORACLE_INET, PLATFORM_ORACLE);
253: jdbcDriverToPlatform
254: .put(DRIVER_POSTGRESQL, PLATFORM_POSTGRESQL);
255: jdbcDriverToPlatform.put(DRIVER_SYBASE, PLATFORM_SYBASE);
256: jdbcDriverToPlatform.put(DRIVER_SYBASE_OLD, PLATFORM_SYBASE);
257: jdbcDriverToPlatform.put(DRIVER_SYBASE_INET, PLATFORM_SYBASE);
258: }
259:
260: /**
261: * Fills parameters of the given {@link JdbcConnectionDescriptor} with metadata
262: * extracted from the given datasource.
263: *
264: * @param jcd The jdbc connection descriptor to fill
265: * @param dataSource The data source
266: * @param username The username required to establish a connection via the data source
267: * Can be empty if the data source does not require it or if one
268: * is specified in the jdbc connection descriptor
269: * @param password The username required to establish a connection via the data source
270: * Can be empty if the data source or username does not require it or if one
271: * is specified in the jdbc connection descriptor
272: */
273: public void fillJCDFromDataSource(JdbcConnectionDescriptor jcd,
274: DataSource dataSource, String username, String password)
275: throws MetadataException {
276: String realUsername = (jcd.getUserName() != null ? jcd
277: .getUserName() : username);
278: String realPassword = (jcd.getPassWord() != null ? jcd
279: .getPassWord() : password);
280: Connection connection = null;
281: DatabaseMetaData metadata = null;
282:
283: try {
284: // we have to open a connection to be able to retrieve metadata
285: if (realUsername != null) {
286: connection = dataSource.getConnection(realUsername,
287: realPassword);
288: } else {
289: connection = dataSource.getConnection();
290: }
291:
292: metadata = connection.getMetaData();
293: } catch (Throwable t) {
294: if (connection != null) {
295: try {
296: connection.close();
297: } catch (SQLException ex) {
298: }
299: }
300: throw new MetadataException(
301: "Could not get the metadata from the given datasource",
302: t);
303: }
304:
305: try {
306: HashMap urlComponents = parseConnectionUrl(metadata
307: .getURL());
308:
309: if (urlComponents.containsKey(PROPERTY_DBALIAS)) {
310: jcd.setProtocol((String) urlComponents
311: .get(PROPERTY_PROTOCOL));
312: jcd.setSubProtocol((String) urlComponents
313: .get(PROPERTY_SUBPROTOCOL));
314: jcd.setDbAlias((String) urlComponents
315: .get(PROPERTY_DBALIAS));
316: if (jdbcSubProtocolToPlatform.containsKey(jcd
317: .getSubProtocol())) {
318: // TODO: We might be able to use this: metadata.getDatabaseProductName();
319: jcd.setDbms((String) jdbcSubProtocolToPlatform
320: .get(jcd.getSubProtocol()));
321: }
322: }
323: } catch (Throwable t) {
324: try {
325: connection.close();
326: } catch (SQLException ex) {
327: }
328: throw new MetadataException(
329: "Could not get the metadata from the given datasource",
330: t);
331: }
332: try {
333: // this will only work with JDK >= 1.4 and only with some jdbc drivers
334: Integer majorVersion = (Integer) PropertyUtils.getProperty(
335: metadata, "JDBCMajorVersion");
336: Integer minorVersion = (Integer) PropertyUtils.getProperty(
337: metadata, "JDBCMinorVersion");
338:
339: jcd.setJdbcLevel(Double.parseDouble(majorVersion.toString()
340: + "." + minorVersion.toString()));
341: } catch (Throwable t) {
342: // otherwise we're assuming JDBC 2.0 compliance
343: jcd.setJdbcLevel(2.0);
344: }
345: try {
346: connection.close();
347: } catch (SQLException ex) {
348: }
349: }
350:
351: /**
352: * Splits the given jdbc connection url into its components and puts them into
353: * a hash map using the <code>PROPERTY_</code> constants.
354: *
355: * @param jdbcConnectionUrl The connection url
356: * @return The properties
357: */
358: public HashMap parseConnectionUrl(String jdbcConnectionUrl) {
359: HashMap result = new HashMap();
360:
361: if (jdbcConnectionUrl == null) {
362: return result;
363: }
364:
365: int pos = jdbcConnectionUrl.indexOf(':');
366: int lastPos;
367:
368: result.put(PROPERTY_PROTOCOL, jdbcConnectionUrl.substring(0,
369: pos));
370:
371: lastPos = pos;
372: pos = jdbcConnectionUrl.indexOf(':', lastPos + 1);
373:
374: String subProtocol = jdbcConnectionUrl.substring(lastPos + 1,
375: pos);
376:
377: // there are a few jdbc drivers that have a subprotocol containing one or more ':'
378: if ("inetpool".equals(subProtocol)) {
379: // Possible forms are:
380: // inetpool:<subprotocol>
381: // inetpool:jdbc:<subprotocol> (where we'll remove the 'jdbc' part)
382:
383: int tmpPos = jdbcConnectionUrl.indexOf(':', pos + 1);
384:
385: if ("inetpool:jdbc".equals(jdbcConnectionUrl.substring(
386: lastPos + 1, tmpPos))) {
387: pos = tmpPos;
388: tmpPos = jdbcConnectionUrl.indexOf(':', pos + 1);
389: }
390: subProtocol += ":"
391: + jdbcConnectionUrl.substring(pos + 1, tmpPos);
392: } else if ("jtds".equals(subProtocol)
393: || "microsoft".equals(subProtocol)
394: || "sybase".equals(subProtocol)) {
395: pos = jdbcConnectionUrl.indexOf(':', pos + 1);
396: subProtocol = ":"
397: + jdbcConnectionUrl.substring(lastPos + 1, pos);
398: }
399:
400: result.put(PROPERTY_SUBPROTOCOL, subProtocol);
401: result.put(PROPERTY_DBALIAS, jdbcConnectionUrl
402: .substring(pos + 1));
403:
404: return result;
405: }
406:
407: /**
408: * Derives the OJB platform to use for a database that is connected via a url using the specified
409: * subprotocol, and where the specified jdbc driver is used.
410: *
411: * @param jdbcSubProtocol The JDBC subprotocol used to connect to the database
412: * @param jdbcDriver The JDBC driver used to connect to the database
413: * @return The platform identifier or <code>null</code> if no platform could be found
414: */
415: public String findPlatformFor(String jdbcSubProtocol,
416: String jdbcDriver) {
417: String platform = (String) jdbcSubProtocolToPlatform
418: .get(jdbcSubProtocol);
419:
420: if (platform == null) {
421: platform = (String) jdbcDriverToPlatform.get(jdbcDriver);
422: }
423: return platform;
424: }
425: }
|