/*
*
* SchemaCrawler
* http://sourceforge.net/projects/schemacrawler
* Copyright (c) 2000-2009, Sualeh Fatehi.
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
package schemacrawler.utility.datasource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* A DataSource that creates connections by reading a properties file.
*
* @author Sualeh Fatehi sualeh@hotmail.com
*/
public final class PropertiesDataSource
implements DataSource
{
/**
* System specific line separator character.
*/
private static final String NEWLINE = System.getProperty("line.separator");
private static final String DRIVER = "driver";
private static final String URL = "url";
private static final String USER = "user";
private static final String PASSWORD = "password";
private static final String DEFAULTCONNECTION = "defaultconnection";
private static final Logger LOGGER = Logger
.getLogger(PropertiesDataSource.class.getName());
private String url;
private Properties connectionParams;
private int loginTimeout;
private Driver jdbcDriver;
private String databaseProductName;
private String databaseProductVersion;
private String driverName;
private String driverVersion;
private PrintWriter logWriter;
/**
* Creates a PropertiesDataSource from a set of connection properties,
* using the default connection.
*
* @param properties
* Connection properties.
* @throws PropertiesDataSourceException
* On any exception in creating the PropertiesDataSource.
*/
public PropertiesDataSource(final Properties properties)
{
constructPropertiesDataSource(properties, null);
}
/**
* Creates a PropertiesDataSource from a set of connection properties,
* using the named connection. If the named connection is null or
* empty, use the default connection.
*
* @param properties
* Connection properties.
* @param connectionName
* The name of the connection to use.
* @throws PropertiesDataSourceException
* On any exception in creating the PropertiesDataSource.
*/
public PropertiesDataSource(final Properties properties,
final String connectionName)
{
constructPropertiesDataSource(properties, connectionName);
}
/**
* Attempts to establish a connection with the data source that this
* <code>DataSource</code> object represents.
*
* @return a connection to the data source
* @throws SQLException
* if a database access error occurs
*/
public Connection getConnection()
throws SQLException
{
final String username = connectionParams.getProperty(USER);
final String password = connectionParams.getProperty(PASSWORD);
return getConnection(username, password);
}
/**
* Attempts to establish a connection with the data source that this
* <code>DataSource</code> object represents.
*
* @param username
* the database user on whose behalf the connection is being
* made
* @param password
* the user's password
* @return a connection to the data source
* @throws SQLException
* if a database access error occurs
*/
public Connection getConnection(final String username, final String password)
throws SQLException
{
if (username == null || password == null)
{
throw new SQLException("Null username or password");
}
final Properties params = new Properties();
params.setProperty(USER, username);
params.setProperty(PASSWORD, password);
return jdbcDriver.connect(url, params);
}
/**
* Gets the name of the JDBC driver class.
*
* @return Name of the JDBC driver class.
*/
public String getJdbcDriverClass()
{
return jdbcDriver.getClass().getName();
}
/**
* Gets the maximum time in seconds that this data source can wait
* while attempting to connect to a database. A value of zero means
* that the timeout is the default system timeout if there is one;
* otherwise, it means that there is no timeout. When a
* <code>DataSource</code> object is created, the login timeout is
* initially zero.
*
* @return the data source login time limit
* @see #setLoginTimeout
*/
public int getLoginTimeout()
{
return loginTimeout;
}
/**
* Retrieves the log writer for this <code>DataSource</code> object.
* The log writer is a character output stream to which all logging
* and tracing messages for this data source will be printed. This
* includes messages printed by the methods of this object, messages
* printed by methods of other objects manufactured by this object,
* and so on. Messages printed to a data source specific log writer
* are not printed to the log writer associated with the
* <code>java.sql.Drivermanager</code> class. When a
* <code>DataSource</code> object is created, the log writer is
* initially null; in other words, the default is for logging to be
* disabled.
*
* @return the log writer for this data source or null if logging is
* disabled
* @see #setLogWriter
*/
public PrintWriter getLogWriter()
{
return logWriter;
}
/**
* Gets the database connection URL.
*
* @return Database connection URL
*/
public String getUrl()
{
return url;
}
/**
* Get the username for the database connection.
*
* @return Username for the database connection
*/
public String getUser()
{
return connectionParams.getProperty(USER);
}
/**
* {@inheritDoc}
*
* @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
*/
public boolean isWrapperFor(final Class<?> iface)
throws SQLException
{
return false;
}
/**
* Sets the maximum time in seconds that this data source will wait
* while attempting to connect to a database. A value of zero
* specifies that the timeout is the default system timeout if there
* is one; otherwise, it specifies that there is no timeout. When a
* <code>DataSource</code> object is created, the login timeout is
* initially zero.
*
* @param seconds
* the data source login time limit
* @see #getLoginTimeout
*/
public void setLoginTimeout(final int seconds)
{
loginTimeout = seconds;
}
/**
* Sets the log writer for this <code>DataSource</code> object to the
* given <code>java.io.PrintWriter</code> object. The log writer is a
* character output stream to which all logging and tracing messages
* for this data source will be printed. This includes messages
* printed by the methods of this object, messages printed by methods
* of other objects manufactured by this object, and so on. Messages
* printed to a data source- specific log writer are not printed to
* the log writer associated with the
* <code>java.sql.Drivermanager</code> class. When a
* <code>DataSource</code> object is created the log writer is
* initially null; in other words, the default is for logging to be
* disabled.
*
* @param out
* the new log writer; to disable logging, set to null
* @see #getLogWriter
*/
public void setLogWriter(final PrintWriter out)
{
if (out != null)
{
logWriter = out;
// DriverManager.setLogWriter(out);
}
}
/**
* {@inheritDoc}
*
* @see Object#toString()
*/
@Override
public String toString()
{
final StringBuffer info = new StringBuffer();
info.append("-- database product: ").append(databaseProductName)
.append(" ").append(databaseProductVersion).append(NEWLINE)
.append("-- driver: ").append(jdbcDriver.getClass().getName())
.append(" - ").append(driverName).append(" ").append(driverVersion)
.append(NEWLINE).append("-- connection: ").append(url);
return info.toString();
}
/**
* {@inheritDoc}
*
* @see java.sql.Wrapper#unwrap(java.lang.Class)
*/
public <T> T unwrap(final Class<T> iface)
throws SQLException
{
throw new SQLException("Not implemented");
}
private void constructPropertiesDataSource(final Properties properties,
final String connectionName)
{
final String defaultConnection = properties.getProperty(DEFAULTCONNECTION,
"");
String useConnectionName = connectionName;
// get the subgroup of the properties for the given connection
if (connectionName == null)
{
useConnectionName = defaultConnection;
}
final GroupedProperties groups = new GroupedProperties(properties);
// check if the connection name is defined
if (!groups.isGroup(useConnectionName))
{
throw new IllegalArgumentException("Connection not defined: "
+ useConnectionName);
}
// create substituted properties
final SubstitutableProperties substitutedProperties = new SubstitutableProperties(groups
.subgroup(useConnectionName));
logWriter = new PrintWriter(System.err);
connectionParams = substitutedProperties;
LOGGER.log(Level.FINE, "Using connection \"" + connectionName + "\"");
LOGGER.log(Level.FINE, getConnectionParamsInfo());
try
{
final String driver = connectionParams.getProperty(DRIVER);
final Class<?> jdbcDriverClass = Class.forName(driver);
jdbcDriver = (Driver) jdbcDriverClass.newInstance();
}
catch (final Exception e)
{
throw new RuntimeException("Driver class not be initialized - "
+ e.getLocalizedMessage(), e);
}
url = connectionParams.getProperty(URL);
testConnection();
}
private String getConnectionParamsInfo()
{
final StringBuffer buffer = new StringBuffer();
buffer.append("Connection parameters:");
final Set<Map.Entry<Object, Object>> entries = connectionParams.entrySet();
for (final Map.Entry<Object, Object> entry: entries)
{
final String key = (String) entry.getKey();
final String value = (String) entry.getValue();
if (!key.equalsIgnoreCase(PASSWORD))
{
buffer.append(NEWLINE).append("-- ").append(key).append(": ")
.append(value);
}
}
return buffer.toString();
}
private void testConnection()
{
LOGGER.log(Level.FINE, "Attempting connection...");
Connection connection = null;
try
{
connection = getConnection();
if (connection == null)
{
throw new RuntimeException("Could not establish a connection");
}
// set metadata properties
final DatabaseMetaData metaData = connection.getMetaData();
databaseProductName = metaData.getDatabaseProductName();
databaseProductVersion = metaData.getDatabaseProductVersion();
driverName = metaData.getDriverName();
driverVersion = metaData.getDriverVersion();
}
catch (final SQLException e)
{
final String errorMessage = e.getMessage();
LOGGER.log(Level.SEVERE, "Could not establish a connection: "
+ errorMessage);
throw new RuntimeException(errorMessage, e);
}
finally
{
try
{
if (connection != null)
{
connection.close();
}
}
catch (final SQLException e)
{
final String errorMessage = e.getMessage();
LOGGER.log(Level.WARNING, "Could not close the connection: "
+ errorMessage);
throw new RuntimeException(errorMessage, e);
}
}
LOGGER.log(Level.FINE, "Connection successful.");
LOGGER.log(Level.INFO, NEWLINE + toString());
}
}