/*
$Header: /cvsroot/xorm/xorm/src/org/xorm/datastore/sql/SQLConnectionInfo.java,v 1.11 2003/09/26 20:23:54 wbiggs Exp $
This file is part of XORM.
XORM is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
XORM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with XORM; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.xorm.datastore.sql;
import java.io.InputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
import javax.jdo.JDOFatalException;
import javax.jdo.JDOFatalUserException;
import org.xorm.I15d;
import org.xorm.ModelMapping;
import org.xorm.datastore.ConnectionInfo;
import org.xorm.datastore.DatastoreDriver;
import org.xorm.datastore.Column;
import org.xorm.datastore.Table;
public class SQLConnectionInfo extends ConnectionInfo implements I15d {
private DataSource dataSource;
private long idleCheck;
private boolean checkReturnedConnection;
private String nextIDStatement;
private String lastIDStatement;
private Class driverImpl;
private Integer isolationLevel;
private String idleCheckSQL;
private boolean executeBatch;
private boolean autoIncrementPrimaryKeys = false;
private String sequenceNamePattern = null;
private static final String PROP_PREFIX = "org.xorm.datastore.sql.";
public static final String OPTION_AUTO_INCREMENT_PRIMARY_KEYS = "org.xorm.datastore.option.autoIncrementPrimaryKeys";
public static final String OPTION_SEQUENCE_NAME_PATTERN = "org.xorm.datastore.option.sequenceNamePattern";
protected static Logger logger = Logger.getLogger("org.xorm.datastore.sql.SQLConnectionInfo");
public void setProperties(Properties properties) {
super.setProperties(properties);
setIdleCheck(Long.parseLong
(properties.getProperty(PROP_PREFIX + "IdleCheck",
"300000"))); // default 5 minutes
setIdleCheckSQL(properties.getProperty(PROP_PREFIX + "IdleCheckSQL"));
setCheckReturnedConnection
(Boolean.valueOf(properties.getProperty
(PROP_PREFIX + "CheckReturnedConnection",
"false")).booleanValue());
setExecuteBatch
(Boolean.valueOf(properties.getProperty
(PROP_PREFIX + "ExecuteBatch",
"false")).booleanValue());
setNextIDStatement(properties.getProperty(PROP_PREFIX + "NextIDStatement"));
setLastIDStatement(properties.getProperty(PROP_PREFIX + "LastIDStatement"));
setIsolationLevelString(properties.getProperty(PROP_PREFIX + "TransactionIsolationLevel"));
autoIncrementPrimaryKeys = "true".equalsIgnoreCase(properties.getProperty(OPTION_AUTO_INCREMENT_PRIMARY_KEYS));
sequenceNamePattern = properties.getProperty(OPTION_SEQUENCE_NAME_PATTERN);
String driverClass = properties.getProperty("org.xorm.datastore.DatastoreDriverClass");
// If driverClass is unspecified, determine it based on
// javax.jdo.option.ConnectionDriverName.
if (driverClass == null) {
if (connectionDriverName != null) {
InputStream stream = getClass().getResourceAsStream("/org/xorm/datastore/sql/drivers.properties");
if (stream != null) {
Properties drivers = new Properties();
try {
drivers.load(stream);
} catch (IOException e) {
throw new JDOFatalException(I18N.msg("E_no_drivers_props"));
}
String driverInfo = drivers.getProperty(connectionDriverName);
if (driverInfo != null) {
String[] driverParts = driverInfo.split(",");
driverClass = driverParts[0];
// Munge the properties
setNextIDStatement(driverParts[1].trim());
setLastIDStatement(driverParts[2].trim());
}
} // could read drivers.properties
} // connectionDriverName was specified
}
if (driverClass != null) {
try {
driverImpl = Class.forName(driverClass);
} catch (ClassNotFoundException e) {
throw new JDOFatalException(I18N.msg("E_no_driver_class", driverClass));
}
} else {
throw new JDOFatalException(I18N.msg("E_no_driver", connectionDriverName));
}
}
/**
* Returns the DataSource configured with the settings above.
* The DataSource is acquired in the following order:
* <ol>
* <li> Casting the ConnectionFactory object to javax.sql.DataSource </li>
* <li> Using JNDI to lookup the ConnectionFactoryName object,
* and casting it to javax.sql.DataSource. </li>
* <li> Creating a XORM PooledDataSource instance and configuring
* it with the other properties (connectionDriverName is required). </li>
* </ol>
* If no DataSource can be acquired using any of the above techniques,
* returns null.
*/
public synchronized DataSource getDataSource() {
if (dataSource == null) {
if (connectionFactory instanceof DataSource) {
dataSource = (DataSource) connectionFactory;
} else if (connectionDriverName == null) {
return null;
} else {
// Use XORM DataSource implementation
dataSource = new PooledDataSource();
PooledDataSource pds = (PooledDataSource) dataSource;
pds.setDriverName(connectionDriverName);
pds.setConnectionUrl(connectionURL);
pds.setUser(connectionUserName);
pds.setPassword(connectionPassword);
pds.setMinPool(minPool);
pds.setMaxPool(maxPool);
pds.setLoginTimeout(msWait);
pds.setIdleCheck(idleCheck);
pds.setIdleCheckSQL(idleCheckSQL);
pds.setCheckReturnedConnection(checkReturnedConnection);
pds.setIsolationLevel(isolationLevel);
}
}
return dataSource;
}
public long getIdleCheck() {
return idleCheck;
}
public void setIdleCheck(long idle) {
idleCheck = idle;
}
public String getIdleCheckSQL() {
return idleCheckSQL;
}
public void setIdleCheckSQL(String sql) {
idleCheckSQL = sql;
}
public boolean getCheckReturnedConnection() {
return checkReturnedConnection;
}
public void setCheckReturnedConnection(boolean check) {
checkReturnedConnection = check;
}
public boolean getExecuteBatch() {
return executeBatch;
}
public void setExecuteBatch(boolean value) {
executeBatch = value;
}
public String getNextIDStatement() {
return nextIDStatement;
}
public void setNextIDStatement(String nextIDStatement) {
this.nextIDStatement = nextIDStatement;
}
public String getLastIDStatement() {
return lastIDStatement;
}
public void setLastIDStatement(String lastIDStatement) {
this.lastIDStatement = lastIDStatement;
}
public void setIsolationLevelString(String level) {
if ("READ_UNCOMMITTED".equals(level)) {
isolationLevel = new Integer(Connection.TRANSACTION_READ_UNCOMMITTED);
} else if ("READ_COMMITTED".equals(level)) {
isolationLevel = new Integer(Connection.TRANSACTION_READ_COMMITTED);
} else if ("REPEATABLE_READ".equals(level)) {
isolationLevel = new Integer(Connection.TRANSACTION_REPEATABLE_READ);
} else if ("SERIALIZABLE".equals(level)) {
isolationLevel = new Integer(Connection.TRANSACTION_SERIALIZABLE);
}
}
public DatastoreDriver getDriver() {
BaseSQLDriver driver = null;
try {
driver = (BaseSQLDriver) driverImpl.newInstance();
} catch (InstantiationException e) {
throw new JDOFatalException(I18N.msg("E_instantiation", driverImpl.getName()));
} catch (IllegalAccessException e) {
throw new JDOFatalException(I18N.msg("E_illegal_access", driverImpl.getName()));
}
driver.setDataSource(getDataSource());
driver.setConnectionInfo(this);
return driver;
}
/**
* Releases the data source associated with this connection
* info.
*/
public synchronized void close() {
if (dataSource instanceof PooledDataSource) {
((PooledDataSource) dataSource).close();
dataSource = null;
}
}
/** Calls close() to release any lingering resources. */
public void finalize() {
close();
}
public Table describeTable(String tableName) {
logger.fine("Attempting to load table from metadata: " + tableName);
Connection connection = null;
DatabaseMetaData metadata = null;
ResultSet results = null;
Table table = null;
try {
connection = getDataSource().getConnection();
metadata = connection.getMetaData();
results = metadata.getTables(null,
null,
tableName,
new String[] { "TABLE" });
// It's possible that multiple tables match the pattern that
// tableName defines. Iterate through until we find the right one.
boolean found = false;
while (!found && results.next()) {
found = results.getString("TABLE_NAME").equals(tableName);
}
if (!found) {
// The table doesn't exist.
logger.warning("Table doesn't exist in metadata: " + tableName);
throw new JDOFatalUserException(I18N.msg("E_no_table_in_db", tableName));
}
// Snag all of the primary key column names, which we'll use
// as we iterate through the tables columns.
results = metadata.getPrimaryKeys(null, null, tableName);
ArrayList pkColumnNames = new ArrayList();
while (results.next()) {
String pkColumnName = results.getString("COLUMN_NAME");
logger.fine("Primary key column: " + pkColumnName);
pkColumnNames.add(pkColumnName);
}
results.close();
table = new Table(tableName);
// Iterate through all of the columns
results = metadata.getColumns(null, null, tableName, null);
while (results.next()) {
String columnName = results.getString("COLUMN_NAME");
int dataType = results.getInt("DATA_TYPE");
String typeName = SQLType.nameFor(dataType);
boolean isNullable =
results.getString("IS_NULLABLE").equalsIgnoreCase("YES");
boolean isPrimaryKey = pkColumnNames.contains(columnName);
logger.fine("Column: " + columnName +
", type: " + typeName + " (" + dataType +
"), nullable: " + isNullable +
(isPrimaryKey ? ", primary key" : ""));
Column column = new Column(table, columnName);
if (isPrimaryKey) {
table.setPrimaryKey(column);
if (autoIncrementPrimaryKeys) {
logger.fine("Setting " + columnName +
" column to auto-increment");
column.setAutoIncremented(true);
}
else if (sequenceNamePattern != null &&
!sequenceNamePattern.equals("")) {
// Take the sequence name pattern and replace any
// instance of "{table}" and/or "{column}" with the
// respective names.
String seqName = sequenceNamePattern
.replaceAll("\\{table\\}", tableName)
.replaceAll("\\{column\\}", columnName);
logger.fine("Using sequence: " + seqName);
column.setSequence(seqName);
}
}
column.setNonNull(!isNullable);
column.setType(typeName);
}
results.close();
}
catch (SQLException e) {
logger.warning("While trying to load \"" + tableName +
"\" table, caught " +
e.getClass().getName() + ": " +
e.getMessage());
throw new JDOFatalException(I18N.msg("E_jdo_sql_exception"), e);
}
finally {
if (results != null) {
try {
results.close();
}
catch (Exception e) {
logger.warning("Can't close ResultSet: " +
e.getClass().getName() + ": " +
e.getMessage());
}
}
if (connection != null) {
try {
connection.close();
}
catch (Exception e) {
logger.warning("Can't close Connection: " +
e.getClass().getName() + ": " +
e.getMessage());
}
}
}
return table;
}
}