/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.jtrac.config;
import java.sql.Connection;
import java.sql.Statement;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.util.StringUtils;
/**
* <p>
* This class implements the Spring frameworks
* {@link org.springframework.beans.factory.FactoryBean} as well as the
* {@link org.springframework.beans.factory.DisposableBean} to conditionally
* create the correct <code>DataSource</code> object to access the database.
* </p>
*
* Supported data sources are:
* <ul>
* <li><a href="http://hsqldb.org/">HSQLDB (embedded)</a></li>
* <li><a href="http://commons.apache.org/dbcp/">Apache DBCP</a></li>
* <li>Java Naming and Directory Interface (JNDI); supported by most JDBC
* database drivers</li>
* </ul>
*/
public class DataSourceFactoryBean implements FactoryBean, DisposableBean {
/**
* Logger object
*/
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* DB driver class name
*/
private String driverClassName;
/**
* DB url
*/
private String url;
/**
* DB user
*/
private String username;
/**
* DB password
*/
private String password;
/**
* DB queries used to check if the database can be accessed
*/
private String validationQuery;
/**
* DB JNDI data source name
*/
private String dataSourceJndiName;
/**
* DB JNDI data source object
*/
private DataSource dataSource;
/**
* This method allows to store the name of the DB driver class.
*
* @param driverClassName The name of the DB driver class.
*/
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
/**
* This method allows to store the DB url.
*
* @param url The DB url.
*/
public void setUrl(String url) {
this.url = url;
}
/**
* This method allows to store the DB user.
*
* @param username The DB user.
*/
public void setUsername(String username) {
this.username = username;
}
/**
* This method allows to store the DB password.
*
* @param password The DB password.
*/
public void setPassword(String password) {
this.password = password;
}
/**
* This method allows to store the SQL query string used to check if
* the DB connection is working.
*
* @param validationQuery The SQL query used to validate if the DB
* connection is working.
*/
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
/**
* This method allows to store the JNDI data source name.
*
* @param dataSourceJndiName The JNDI data source name.
*/
public void setDataSourceJndiName(String dataSourceJndiName) {
this.dataSourceJndiName = dataSourceJndiName;
}
/**
* This method returns the dataSource object used for the DB access.
*
* @throws Exception
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public Object getObject() throws Exception {
if(StringUtils.hasText(dataSourceJndiName)) {
logger.info("JNDI datasource requested, looking up datasource from JNDI name: '" + dataSourceJndiName + "'");
JndiObjectFactoryBean factoryBean = new JndiObjectFactoryBean();
factoryBean.setJndiName(dataSourceJndiName);
// "java:comp/env/" will be prefixed if the JNDI name doesn't already have it
factoryBean.setResourceRef(true);
// This step actually does the JNDI lookup
try {
factoryBean.afterPropertiesSet();
} catch(Exception e) {
logger.error("datasource init from JNDI failed : " + e);
logger.error("aborting application startup");
throw new RuntimeException(e);
} // end try..catch
dataSource = (DataSource) factoryBean.getObject();
} else if(url.startsWith("jdbc:hsqldb:file")) {
logger.info("embedded HSQLDB mode detected, switching on spring single connection data source");
SingleConnectionDataSource ds = new SingleConnectionDataSource();
ds.setUrl(url);
ds.setDriverClassName(driverClassName);
ds.setUsername(username);
ds.setPassword(password);
ds.setSuppressClose(true);
dataSource = ds;
} else {
logger.info("Not using embedded HSQLDB or JNDI datasource, switching on Apache DBCP data source connection pooling");
BasicDataSource ds = new BasicDataSource();
ds.setUrl(url);
ds.setDriverClassName(driverClassName);
ds.setUsername(username);
ds.setPassword(password);
ds.setValidationQuery(validationQuery);
ds.setTestOnBorrow(false);
ds.setTestWhileIdle(true);
ds.setTimeBetweenEvictionRunsMillis(600000);
dataSource = ds;
} // end if..else
return dataSource;
}
/**
* This method returns the class name of the DataSource object which can be
* used to determine which data source implementation is currently used.
*
* @return Returns the class name <code>Object.class</code> of the
* DataSource object.
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public Class getObjectType() {
return DataSource.class;
}
/**
* This method returns if the factory is implemented as Singleton.
*
* @return Returns if the factory is implemented as Singleton or not.
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return true;
}
/**
* This method is called to clean up the references when the object is
* destroyed.
*
* @throws Exception
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
if(dataSource instanceof SingleConnectionDataSource) {
logger.info("attempting to shut down embedded HSQLDB database");
Connection con = dataSource.getConnection();
Statement stmt = con.createStatement();
stmt.executeUpdate("SHUTDOWN");
stmt.close();
con.close();
logger.info("embedded HSQLDB database shut down successfully");
} else if (dataSource instanceof BasicDataSource){
logger.info("attempting to close Apache DBCP data source");
((BasicDataSource) dataSource).close();
logger.info("Apache DBCP data source closed successfully");
} else {
logger.info("context shutting down for JNDI datasource");
} // end if..else
}
}