Hi,
I am using Hibernate 3.0 with Spring and Weblogic. I use a connection provider that will internally lookup a datasource either using JNDI or DBCP based on whether I am running the application in container or out of container. I use the a servlet filter in in container to bind the session factory against the thread local. However, closing the session in the servlet filter doesn't call the connection provider to close the connection and hance causes connection leak. Any help would be highly appreciated.
Code for the connection provider:
---------------------------------------
Code:
public abstract class DataSourceConnectionProvider implements
ConnectionProvider {
// Logger
private static Log log = LogFactory
.getLog(DataSourceConnectionProvider.class);
// Datasource
private String dsBeanName;
/**
* Initializes the datasource.
*/
public DataSourceConnectionProvider() {
dsBeanName = getDataSourceName();
}
/**
* Does nothing.
*
* @param arg
* @throws HibernateException
* @see org.hibernate.connection.ConnectionProvider#configure(java.util.Properties)
*/
public void configure(Properties arg) throws HibernateException {
}
/**
* Gets the connection.
*
* @return Connection
* @throws SQLException
* @see org.hibernate.connection.ConnectionProvider#getConnection()
*/
public Connection getConnection() throws SQLException {
try {
DataSource ds = (DataSource) ComponentFactory.getBean(dsBeanName);
return ds.getConnection();
} finally {
if (log.isInfoEnabled()) {
log.info("Connection retrieved");
}
}
}
/**
* Closes the connection.
*
* @param con
* @throws SQLException
* @see org.hibernate.connection.ConnectionProvider#closeConnection(java.sql.Connection)
*/
public void closeConnection(Connection con) throws SQLException {
con.close();
if (log.isInfoEnabled()) {
log.info("Connection closed");
}
}
/**
* Do nothing.
* @throws HibernateException.
* @see DataSourceConnectionProvider.close
*/
public void close() throws HibernateException {
}
/**
* Should be overridden by the sub-classes.
*
* @return Datasource bean name.
*/
protected abstract String getDataSourceName();
}
Code for the creating session factory
------------------------------------------
Code:
public class HibernateConfiguration {
// Logger
private static final Log LOG = LogFactory
.getLog(HibernateConfiguration.class);
// Session factory
private SessionFactory sessionFactory;
/**
* Initializes the session factory.
*
* @param mappings MappingLocations.
* @param props Configuration to use.
* @param conProviderClass Hibernate connection provider class.
*/
public HibernateConfiguration(Class[] mappings, Properties props,
Class conProviderClass) {
if (!ConnectionProvider.class.isAssignableFrom(conProviderClass)) {
throw new IllegalArgumentException("Invalid connection provider");
}
Configuration config = new Configuration();
config.setProperties(props);
config.setProperty(Environment.CONNECTION_PROVIDER, conProviderClass
.getName());
for (int i = 0; i < mappings.length; i++) {
config.addClass(mappings[i]);
}
sessionFactory = config.buildSessionFactory();
}
/**
* Gets a new session.
*
* @return Returns a new session.
*/
public Session getSession() {
return sessionFactory.openSession();
}
}
Code for accessing the session
------------------------------------
Code:
public class HibernateUtil {
// Bean name under which hibernate configuration is available
public static final String CONFIG_SUFFIX = "hibernateConfig";
// Logger
private static final Log LOG = LogFactory.getLog(HibernateUtil.class);
// Map of domain names to utility instanes
private static final Map UTIL_MAP = new HashMap();
// Session factory that is used
private HibernateConfiguration config = null;
// Thread-local sessions
private final ThreadLocal sessions = new ThreadLocal();
// Thread-local sessions
private final ThreadLocal transactions = new ThreadLocal();
// Domain name
private String domainName;
/**
* Creates the utility singleton for the domain
* @param domainName
*/
private HibernateUtil(String domainName) {
this.domainName = domainName;
String beanName = domainName + "." + CONFIG_SUFFIX;
config = (HibernateConfiguration)ComponentFactory.getBean(beanName);
}
/**
* Returns the singleton hibernate utility for the given domain.
*
* @param domainName Domain name.
* @return Hibernate utility.
*/
public static final HibernateUtil getInstance(String domainName) {
HibernateUtil util = null;
synchronized(HibernateUtil.class) {
util = (HibernateUtil)UTIL_MAP.get(domainName);
if(util == null) {
util = new HibernateUtil(domainName);
UTIL_MAP.put(domainName, util);
}
}
return util;
}
/**
* Gets the current session.
*
* If there is no current session a new session is created. This method
* is thread safe as the session is bound to the threadocal.
*
* @return Current session.
*/
public Session currentSession() {
Session session = (Session) sessions.get();
if(session == null) {
return getSession();
}else if(!session.isOpen()) {
// Session has already been closed - clean up
sessions.set(null);
return getSession();
}
return session;
}
/**
* Flushes and closes the current session.
*
* The current session is also unbound from the thread-local. This
* method is not required in a managed environment as from Hibernate 3 this
* is automatically done as part of transaction synchronization.
*
* @return Current session.
*/
public void closeSession() {
Session session = (Session) sessions.get();
if (session != null) {
try {
// TODO We should not be doing this.
session.connection().close();
}catch(SQLException ex) {
LOG.fatal(ex.getMessage(), ex);
}
session.close();
if(LOG.isInfoEnabled()) {
LOG.info("Connection closed for domain " + domainName);
}
}
sessions.set(null);
transactions.set(null);
}
/**
* Begins the transaction.
*
* @return Begins the transaction.
*/
public void beginTransaction() {
transactions.set(currentSession().beginTransaction());
}
/**
* Commits the transaction.
*
* @return Commits the transaction.
*/
public void commitTransaction() {
Transaction tx = (Transaction)transactions.get();
if(tx != null) {
getSession().flush();
tx.commit();
transactions.set(null);
}
}
/**
* Rolls back the transaction.
*
* @return Rolls back the transaction.
*/
public void rollbackTransaction() {
Transaction tx = (Transaction)transactions.get();
if(tx != null) {
getSession().flush();
tx.rollback();
transactions.set(null);
}
}
/**
* Flushes the current session.
*
*/
public void flush() {
Session session = getSession();
if(session != null) {
session.flush();
}
}
/*
* Gets a new session.
*/
private Session getSession() {
Session session = config.getSession();
sessions.set(session);
return session;
}
}
Code for closing the session
---------------------------------
Code:
public class HibernateSessionFilter extends AbstractHttpFilter {
// Logger
private static final Log LOG = LogFactory.getLog(HibernateSessionFilter.class);
// Init parameter specifying the domains served by this filter
private static final String DOMAIN_NAMES = "domain.names";
// Domain names
private String[] domainNames;
/**
* Initializes the hibernate domain names.
*/
public void init(FilterConfig config) throws ServletException {
String dns = config.getInitParameter(DOMAIN_NAMES);
if(LOG.isInfoEnabled()) {
LOG.info("Hibernate configurations: " + dns);
}
StringTokenizer tok = new StringTokenizer(dns, ",");
domainNames = new String[tok.countTokens()];
for(int i = 0;i < domainNames.length;i++) {
domainNames[i] = tok.nextToken();
if(LOG.isInfoEnabled()) {
LOG.info("Hibernate configuration: " + domainNames[i]);
}
}
if(LOG.isInfoEnabled()) {
LOG.info("Number of hibernate utilities initialized: " + domainNames.length);
}
}
/**
* Filters the request to make sure hibernate sessions are closed.
*
* @param request In coming request.
* @param response Out going response.
* @param chain Filter chain.
*
*/
public void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
openSessions(request);
try {
chain.doFilter(request, response);
}finally {
closeSessions(request);
}
}
/**
* @param request
*/
private void closeSessions(HttpServletRequest request) {
for(int i = 0;i < domainNames.length;i++) {
HibernateUtil.getInstance(domainNames[i]).closeSession();
if(LOG.isInfoEnabled()) {
LOG.info("Session " + domainNames[i] +
" closed for request: " + request.getRequestURI());
}
}
}
/**
* @param request
*/
private void openSessions(HttpServletRequest request) {
for(int i = 0;i < domainNames.length;i++) {
HibernateUtil.getInstance(domainNames[i]).currentSession();
if(LOG.isInfoEnabled()) {
LOG.info("Session " + domainNames[i] +
" opened for request: " + request.getRequestURI());
}
}
}
}