001: /*
002: $Header: /cvsroot/xorm/xorm/src/org/xorm/datastore/sql/SQLConnectionInfo.java,v 1.11 2003/09/26 20:23:54 wbiggs Exp $
003:
004: This file is part of XORM.
005:
006: XORM is free software; you can redistribute it and/or modify
007: it under the terms of the GNU General Public License as published by
008: the Free Software Foundation; either version 2 of the License, or
009: (at your option) any later version.
010:
011: XORM is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU General Public License for more details.
015:
016: You should have received a copy of the GNU General Public License
017: along with XORM; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package org.xorm.datastore.sql;
021:
022: import java.io.InputStream;
023: import java.io.IOException;
024: import java.sql.Connection;
025: import java.sql.DatabaseMetaData;
026: import java.sql.ResultSet;
027: import java.sql.SQLException;
028: import java.util.ArrayList;
029: import java.util.Iterator;
030: import java.util.Properties;
031: import java.util.Set;
032: import java.util.logging.Level;
033: import java.util.logging.Logger;
034:
035: import javax.sql.DataSource;
036:
037: import javax.jdo.JDOFatalException;
038: import javax.jdo.JDOFatalUserException;
039:
040: import org.xorm.I15d;
041: import org.xorm.ModelMapping;
042: import org.xorm.datastore.ConnectionInfo;
043: import org.xorm.datastore.DatastoreDriver;
044: import org.xorm.datastore.Column;
045: import org.xorm.datastore.Table;
046:
047: public class SQLConnectionInfo extends ConnectionInfo implements I15d {
048: private DataSource dataSource;
049: private long idleCheck;
050: private boolean checkReturnedConnection;
051: private String nextIDStatement;
052: private String lastIDStatement;
053: private Class driverImpl;
054: private Integer isolationLevel;
055: private String idleCheckSQL;
056: private boolean executeBatch;
057: private boolean autoIncrementPrimaryKeys = false;
058: private String sequenceNamePattern = null;
059:
060: private static final String PROP_PREFIX = "org.xorm.datastore.sql.";
061: public static final String OPTION_AUTO_INCREMENT_PRIMARY_KEYS = "org.xorm.datastore.option.autoIncrementPrimaryKeys";
062: public static final String OPTION_SEQUENCE_NAME_PATTERN = "org.xorm.datastore.option.sequenceNamePattern";
063:
064: protected static Logger logger = Logger
065: .getLogger("org.xorm.datastore.sql.SQLConnectionInfo");
066:
067: public void setProperties(Properties properties) {
068: super .setProperties(properties);
069: setIdleCheck(Long.parseLong(properties.getProperty(PROP_PREFIX
070: + "IdleCheck", "300000"))); // default 5 minutes
071: setIdleCheckSQL(properties.getProperty(PROP_PREFIX
072: + "IdleCheckSQL"));
073: setCheckReturnedConnection(Boolean.valueOf(
074: properties.getProperty(PROP_PREFIX
075: + "CheckReturnedConnection", "false"))
076: .booleanValue());
077: setExecuteBatch(Boolean.valueOf(
078: properties.getProperty(PROP_PREFIX + "ExecuteBatch",
079: "false")).booleanValue());
080: setNextIDStatement(properties.getProperty(PROP_PREFIX
081: + "NextIDStatement"));
082: setLastIDStatement(properties.getProperty(PROP_PREFIX
083: + "LastIDStatement"));
084: setIsolationLevelString(properties.getProperty(PROP_PREFIX
085: + "TransactionIsolationLevel"));
086:
087: autoIncrementPrimaryKeys = "true".equalsIgnoreCase(properties
088: .getProperty(OPTION_AUTO_INCREMENT_PRIMARY_KEYS));
089: sequenceNamePattern = properties
090: .getProperty(OPTION_SEQUENCE_NAME_PATTERN);
091:
092: String driverClass = properties
093: .getProperty("org.xorm.datastore.DatastoreDriverClass");
094: // If driverClass is unspecified, determine it based on
095: // javax.jdo.option.ConnectionDriverName.
096: if (driverClass == null) {
097: if (connectionDriverName != null) {
098: InputStream stream = getClass().getResourceAsStream(
099: "/org/xorm/datastore/sql/drivers.properties");
100: if (stream != null) {
101: Properties drivers = new Properties();
102: try {
103: drivers.load(stream);
104: } catch (IOException e) {
105: throw new JDOFatalException(I18N
106: .msg("E_no_drivers_props"));
107: }
108:
109: String driverInfo = drivers
110: .getProperty(connectionDriverName);
111: if (driverInfo != null) {
112: String[] driverParts = driverInfo.split(",");
113: driverClass = driverParts[0];
114: // Munge the properties
115: setNextIDStatement(driverParts[1].trim());
116: setLastIDStatement(driverParts[2].trim());
117: }
118: } // could read drivers.properties
119: } // connectionDriverName was specified
120: }
121: if (driverClass != null) {
122: try {
123: driverImpl = Class.forName(driverClass);
124: } catch (ClassNotFoundException e) {
125: throw new JDOFatalException(I18N.msg(
126: "E_no_driver_class", driverClass));
127: }
128: } else {
129: throw new JDOFatalException(I18N.msg("E_no_driver",
130: connectionDriverName));
131: }
132: }
133:
134: /**
135: * Returns the DataSource configured with the settings above.
136: * The DataSource is acquired in the following order:
137: * <ol>
138: * <li> Casting the ConnectionFactory object to javax.sql.DataSource </li>
139: * <li> Using JNDI to lookup the ConnectionFactoryName object,
140: * and casting it to javax.sql.DataSource. </li>
141: * <li> Creating a XORM PooledDataSource instance and configuring
142: * it with the other properties (connectionDriverName is required). </li>
143: * </ol>
144: * If no DataSource can be acquired using any of the above techniques,
145: * returns null.
146: */
147: public synchronized DataSource getDataSource() {
148: if (dataSource == null) {
149: if (connectionFactory instanceof DataSource) {
150: dataSource = (DataSource) connectionFactory;
151: } else if (connectionDriverName == null) {
152: return null;
153: } else {
154: // Use XORM DataSource implementation
155: dataSource = new PooledDataSource();
156: PooledDataSource pds = (PooledDataSource) dataSource;
157: pds.setDriverName(connectionDriverName);
158: pds.setConnectionUrl(connectionURL);
159: pds.setUser(connectionUserName);
160: pds.setPassword(connectionPassword);
161: pds.setMinPool(minPool);
162: pds.setMaxPool(maxPool);
163: pds.setLoginTimeout(msWait);
164: pds.setIdleCheck(idleCheck);
165: pds.setIdleCheckSQL(idleCheckSQL);
166: pds.setCheckReturnedConnection(checkReturnedConnection);
167: pds.setIsolationLevel(isolationLevel);
168: }
169: }
170: return dataSource;
171: }
172:
173: public long getIdleCheck() {
174: return idleCheck;
175: }
176:
177: public void setIdleCheck(long idle) {
178: idleCheck = idle;
179: }
180:
181: public String getIdleCheckSQL() {
182: return idleCheckSQL;
183: }
184:
185: public void setIdleCheckSQL(String sql) {
186: idleCheckSQL = sql;
187: }
188:
189: public boolean getCheckReturnedConnection() {
190: return checkReturnedConnection;
191: }
192:
193: public void setCheckReturnedConnection(boolean check) {
194: checkReturnedConnection = check;
195: }
196:
197: public boolean getExecuteBatch() {
198: return executeBatch;
199: }
200:
201: public void setExecuteBatch(boolean value) {
202: executeBatch = value;
203: }
204:
205: public String getNextIDStatement() {
206: return nextIDStatement;
207: }
208:
209: public void setNextIDStatement(String nextIDStatement) {
210: this .nextIDStatement = nextIDStatement;
211: }
212:
213: public String getLastIDStatement() {
214: return lastIDStatement;
215: }
216:
217: public void setLastIDStatement(String lastIDStatement) {
218: this .lastIDStatement = lastIDStatement;
219: }
220:
221: public void setIsolationLevelString(String level) {
222: if ("READ_UNCOMMITTED".equals(level)) {
223: isolationLevel = new Integer(
224: Connection.TRANSACTION_READ_UNCOMMITTED);
225: } else if ("READ_COMMITTED".equals(level)) {
226: isolationLevel = new Integer(
227: Connection.TRANSACTION_READ_COMMITTED);
228: } else if ("REPEATABLE_READ".equals(level)) {
229: isolationLevel = new Integer(
230: Connection.TRANSACTION_REPEATABLE_READ);
231: } else if ("SERIALIZABLE".equals(level)) {
232: isolationLevel = new Integer(
233: Connection.TRANSACTION_SERIALIZABLE);
234: }
235:
236: }
237:
238: public DatastoreDriver getDriver() {
239: BaseSQLDriver driver = null;
240: try {
241: driver = (BaseSQLDriver) driverImpl.newInstance();
242: } catch (InstantiationException e) {
243: throw new JDOFatalException(I18N.msg("E_instantiation",
244: driverImpl.getName()));
245: } catch (IllegalAccessException e) {
246: throw new JDOFatalException(I18N.msg("E_illegal_access",
247: driverImpl.getName()));
248: }
249:
250: driver.setDataSource(getDataSource());
251: driver.setConnectionInfo(this );
252: return driver;
253: }
254:
255: /**
256: * Releases the data source associated with this connection
257: * info.
258: */
259: public synchronized void close() {
260: if (dataSource instanceof PooledDataSource) {
261: ((PooledDataSource) dataSource).close();
262: dataSource = null;
263: }
264: }
265:
266: /** Calls close() to release any lingering resources. */
267: public void finalize() {
268: close();
269: }
270:
271: public Table describeTable(String tableName) {
272: logger.fine("Attempting to load table from metadata: "
273: + tableName);
274: Connection connection = null;
275: DatabaseMetaData metadata = null;
276: ResultSet results = null;
277: Table table = null;
278: try {
279: connection = getDataSource().getConnection();
280: metadata = connection.getMetaData();
281: results = metadata.getTables(null, null, tableName,
282: new String[] { "TABLE" });
283: // It's possible that multiple tables match the pattern that
284: // tableName defines. Iterate through until we find the right one.
285: boolean found = false;
286: while (!found && results.next()) {
287: found = results.getString("TABLE_NAME").equals(
288: tableName);
289: }
290: if (!found) {
291: // The table doesn't exist.
292: logger.warning("Table doesn't exist in metadata: "
293: + tableName);
294: throw new JDOFatalUserException(I18N.msg(
295: "E_no_table_in_db", tableName));
296: }
297:
298: // Snag all of the primary key column names, which we'll use
299: // as we iterate through the tables columns.
300: results = metadata.getPrimaryKeys(null, null, tableName);
301: ArrayList pkColumnNames = new ArrayList();
302: while (results.next()) {
303: String pkColumnName = results.getString("COLUMN_NAME");
304: logger.fine("Primary key column: " + pkColumnName);
305: pkColumnNames.add(pkColumnName);
306: }
307: results.close();
308:
309: table = new Table(tableName);
310:
311: // Iterate through all of the columns
312: results = metadata.getColumns(null, null, tableName, null);
313: while (results.next()) {
314: String columnName = results.getString("COLUMN_NAME");
315: int dataType = results.getInt("DATA_TYPE");
316: String typeName = SQLType.nameFor(dataType);
317: boolean isNullable = results.getString("IS_NULLABLE")
318: .equalsIgnoreCase("YES");
319: boolean isPrimaryKey = pkColumnNames
320: .contains(columnName);
321: logger.fine("Column: " + columnName + ", type: "
322: + typeName + " (" + dataType + "), nullable: "
323: + isNullable
324: + (isPrimaryKey ? ", primary key" : ""));
325:
326: Column column = new Column(table, columnName);
327: if (isPrimaryKey) {
328: table.setPrimaryKey(column);
329: if (autoIncrementPrimaryKeys) {
330: logger.fine("Setting " + columnName
331: + " column to auto-increment");
332: column.setAutoIncremented(true);
333: } else if (sequenceNamePattern != null
334: && !sequenceNamePattern.equals("")) {
335: // Take the sequence name pattern and replace any
336: // instance of "{table}" and/or "{column}" with the
337: // respective names.
338: String seqName = sequenceNamePattern
339: .replaceAll("\\{table\\}", tableName)
340: .replaceAll("\\{column\\}", columnName);
341: logger.fine("Using sequence: " + seqName);
342: column.setSequence(seqName);
343: }
344: }
345: column.setNonNull(!isNullable);
346: column.setType(typeName);
347: }
348: results.close();
349: } catch (SQLException e) {
350: logger.warning("While trying to load \"" + tableName
351: + "\" table, caught " + e.getClass().getName()
352: + ": " + e.getMessage());
353: throw new JDOFatalException(
354: I18N.msg("E_jdo_sql_exception"), e);
355: } finally {
356: if (results != null) {
357: try {
358: results.close();
359: } catch (Exception e) {
360: logger.warning("Can't close ResultSet: "
361: + e.getClass().getName() + ": "
362: + e.getMessage());
363: }
364: }
365: if (connection != null) {
366: try {
367: connection.close();
368: } catch (Exception e) {
369: logger.warning("Can't close Connection: "
370: + e.getClass().getName() + ": "
371: + e.getMessage());
372: }
373: }
374: }
375: return table;
376: }
377: }
|