HHH-2884 : SessionFactory close notifications

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14452 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2008-03-18 07:12:54 +00:00
parent e4958dcc31
commit adf3311e58
7 changed files with 358 additions and 69 deletions

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate;
import java.io.Serializable;
/**
* Allows reaction to basic {@link SessionFactory} occurrences.
*
* @author Steve Ebersole
*/
public interface SessionFactoryObserver extends Serializable {
/**
* Callback to indicate that the given factory has been created and is now ready for use.
*
* @param factory The factory initialized.
*/
public void sessionFactoryCreated(SessionFactory factory);
/**
* Callback to indicate that the given factory has been closed. Care should be taken
* in how (if at all) the passed factory reference is used since it is closed.
*
* @param factory The factory closed.
*/
public void sessionFactoryClosed(SessionFactory factory);
}

View File

@ -26,11 +26,15 @@ import java.util.TreeMap;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.dom4j.Attribute;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.hibernate.EmptyInterceptor;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
@ -38,7 +42,7 @@ import org.hibernate.InvalidMappingException;
import org.hibernate.MappingException;
import org.hibernate.MappingNotFoundException;
import org.hibernate.SessionFactory;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.function.SQLFunction;
@ -56,10 +60,16 @@ import org.hibernate.event.LoadEventListener;
import org.hibernate.event.LockEventListener;
import org.hibernate.event.MergeEventListener;
import org.hibernate.event.PersistEventListener;
import org.hibernate.event.PostCollectionRecreateEventListener;
import org.hibernate.event.PostCollectionRemoveEventListener;
import org.hibernate.event.PostCollectionUpdateEventListener;
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostLoadEventListener;
import org.hibernate.event.PostUpdateEventListener;
import org.hibernate.event.PreCollectionRecreateEventListener;
import org.hibernate.event.PreCollectionRemoveEventListener;
import org.hibernate.event.PreCollectionUpdateEventListener;
import org.hibernate.event.PreDeleteEventListener;
import org.hibernate.event.PreInsertEventListener;
import org.hibernate.event.PreLoadEventListener;
@ -67,12 +77,6 @@ import org.hibernate.event.PreUpdateEventListener;
import org.hibernate.event.RefreshEventListener;
import org.hibernate.event.ReplicateEventListener;
import org.hibernate.event.SaveOrUpdateEventListener;
import org.hibernate.event.PreCollectionRecreateEventListener;
import org.hibernate.event.PreCollectionRemoveEventListener;
import org.hibernate.event.PreCollectionUpdateEventListener;
import org.hibernate.event.PostCollectionUpdateEventListener;
import org.hibernate.event.PostCollectionRemoveEventListener;
import org.hibernate.event.PostCollectionRecreateEventListener;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.impl.SessionFactoryImpl;
@ -87,6 +91,7 @@ import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.secure.JACCConfiguration;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
import org.hibernate.tool.hbm2ddl.TableMetadata;
@ -95,14 +100,11 @@ import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.ConfigHelper;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.SerializationHelper;
import org.hibernate.util.StringHelper;
import org.hibernate.util.XMLHelper;
import org.hibernate.util.PropertiesHelper;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
/**
* An instance of <tt>Configuration</tt> allows the application
@ -158,6 +160,8 @@ public class Configuration implements Serializable {
protected final SettingsFactory settingsFactory;
private SessionFactoryObserver sessionFactoryObserver;
protected void reset() {
classes = new HashMap();
imports = new HashMap();
@ -1301,7 +1305,8 @@ public class Configuration implements Serializable {
this,
mapping,
settings,
getInitializedEventListeners()
getInitializedEventListeners(),
sessionFactoryObserver
);
}
@ -2164,4 +2169,12 @@ public class Configuration implements Serializable {
public void addSqlFunction(String functionName, SQLFunction function) {
sqlFunctions.put( functionName, function );
}
public SessionFactoryObserver getSessionFactoryObserver() {
return sessionFactoryObserver;
}
public void setSessionFactoryObserver(SessionFactoryObserver sessionFactoryObserver) {
this.sessionFactoryObserver = sessionFactoryObserver;
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.event;
/**
* Contract for listeners which require notification of SessionFactory closing,
* presumably to destroy internal state.
*
* @author Steve Ebersole
*/
public interface Destructible {
/**
* Notification of {@link org.hibernate.SessionFactory} shutdown.
*/
public void cleanup();
}

View File

@ -1,4 +1,26 @@
//$Id: EventListeners.java 8416 2005-10-16 13:27:54Z epbernard $
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.event;
import java.io.Serializable;
@ -132,6 +154,69 @@ public class EventListeners extends Cloneable implements Serializable {
return clazz;
}
private static interface ListenerProcesser {
public void processListener(Object listener);
}
private void processListeners(ListenerProcesser processer) {
Field[] fields = getClass().getDeclaredFields();
for ( int i = 0; i < fields.length; i++ ) {
try {
final Object field = fields[i].get( this );
if ( field instanceof Object[] ) {
final Object[] listeners = ( Object[] ) field;
int length = listeners.length;
for ( int index = 0 ; index < length ; index++ ) {
processer.processListener( listeners[index ] );
}
}
}
catch ( Exception e ) {
throw new AssertionFailure( "could not process listeners" );
}
}
}
/**
* Call {@link Initializable#initialize} on any listeners that implement the
* {@link Initializable} interface.
*
* @param cfg The configuration.
*/
public void initializeListeners(final Configuration cfg) {
try {
processListeners(
new ListenerProcesser() {
public void processListener(Object listener) {
if ( listener instanceof Initializable ) {
( ( Initializable ) listener ).initialize( cfg );
}
}
}
);
}
catch ( Exception e ) {
throw new AssertionFailure("could not init listeners");
}
}
public void destroyListeners() {
try {
processListeners(
new ListenerProcesser() {
public void processListener(Object listener) {
if ( listener instanceof Destructible ) {
( ( Destructible ) listener ).cleanup();
}
}
}
);
}
catch ( Exception e ) {
throw new AssertionFailure("could not init listeners");
}
}
public LoadEventListener[] getLoadEventListeners() {
return loadEventListeners;
}
@ -388,39 +473,6 @@ public class EventListeners extends Cloneable implements Serializable {
this.preUpdateEventListeners = preUpdateEventListener;
}
/**
* Call <tt>initialize()</tt> on any listeners that implement
* <tt>Initializable</tt>.
* @see Initializable
*/
public void initializeListeners(Configuration cfg) {
Field[] fields = getClass().getDeclaredFields();
for ( int i = 0; i < fields.length; i++ ) {
Object[] listeners;
try {
Object listener = fields[i].get(this);
if (listener instanceof Object[]) {
listeners = (Object[]) listener;
}
else {
continue;
}
}
catch (Exception e) {
throw new AssertionFailure("could not init listeners");
}
int length = listeners.length;
for (int index = 0 ; index < length ; index++) {
Object listener = listeners[index];
if (listener instanceof Initializable ) {
( (Initializable) listener ).initialize(cfg);
}
}
}
}
public PostDeleteEventListener[] getPostCommitDeleteEventListeners() {
return postCommitDeleteEventListeners;
}

View File

@ -1,11 +1,34 @@
//$Id: Initializable.java 7793 2005-08-10 05:06:40Z oneovthafew $
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.event;
import org.hibernate.cfg.Configuration;
/**
* An event listener that requires access to mappings to
* initialize state at initialization time.
* An event listener that requires access to mappings to initialize state at
* initialization time.
*
* @author Gavin King
*/
public interface Initializable {

View File

@ -16,7 +16,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
@ -24,6 +23,7 @@ import javax.transaction.TransactionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.AssertionFailure;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.EntityMode;
@ -34,17 +34,17 @@ import org.hibernate.ObjectNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.cache.CacheKey;
import org.hibernate.cache.QueryCache;
import org.hibernate.cache.UpdateTimestampsCache;
import org.hibernate.cache.Region;
import org.hibernate.cache.EntityRegion;
import org.hibernate.cache.CollectionRegion;
import org.hibernate.cache.impl.CacheDataDescriptionImpl;
import org.hibernate.cache.access.EntityRegionAccessStrategy;
import org.hibernate.cache.EntityRegion;
import org.hibernate.cache.QueryCache;
import org.hibernate.cache.Region;
import org.hibernate.cache.UpdateTimestampsCache;
import org.hibernate.cache.access.AccessType;
import org.hibernate.cache.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.access.EntityRegionAccessStrategy;
import org.hibernate.cache.impl.CacheDataDescriptionImpl;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Settings;
@ -62,6 +62,7 @@ import org.hibernate.engine.NamedSQLQueryDefinition;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.query.QueryPlanCache;
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
import org.hibernate.event.EventListeners;
import org.hibernate.exception.SQLExceptionConverter;
import org.hibernate.id.IdentifierGenerator;
@ -116,6 +117,9 @@ import org.hibernate.util.ReflectHelper;
*/
public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor {
private static final Logger log = LoggerFactory.getLogger(SessionFactoryImpl.class);
private static final IdentifierGenerator UUID_GENERATOR = new UUIDHexGenerator();
private final String name;
private final String uuid;
@ -144,22 +148,18 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
private final transient CurrentSessionContext currentSessionContext;
private final transient EntityNotFoundDelegate entityNotFoundDelegate;
private final transient SQLFunctionRegistry sqlFunctionRegistry;
private final transient SessionFactoryObserver observer;
private final QueryPlanCache queryPlanCache = new QueryPlanCache( this );
private transient boolean isClosed = false;
private static final IdentifierGenerator UUID_GENERATOR = new UUIDHexGenerator();
private static final Logger log = LoggerFactory.getLogger(SessionFactoryImpl.class);
public SessionFactoryImpl(
Configuration cfg,
Mapping mapping,
Settings settings,
EventListeners listeners)
throws HibernateException {
EventListeners listeners,
SessionFactoryObserver observer) throws HibernateException {
log.info("building session factory");
@ -169,6 +169,12 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
this.settings = settings;
this.sqlFunctionRegistry = new SQLFunctionRegistry(settings.getDialect(), cfg.getSqlFunctions());
this.eventListeners = listeners;
this.observer = observer != null ? observer : new SessionFactoryObserver() {
public void sessionFactoryCreated(SessionFactory factory) {
}
public void sessionFactoryClosed(SessionFactory factory) {
}
};
this.filters = new HashMap();
this.filters.putAll( cfg.getFilterDefinitions() );
@ -382,6 +388,8 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
};
}
this.entityNotFoundDelegate = entityNotFoundDelegate;
this.observer.sessionFactoryCreated( this );
}
public QueryPlanCache getQueryPlanCache() {
@ -766,6 +774,11 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
*/
public void close() throws HibernateException {
if ( isClosed ) {
log.trace( "already closed" );
return;
}
log.info("closing");
isClosed = true;
@ -810,6 +823,8 @@ public final class SessionFactoryImpl implements SessionFactory, SessionFactoryI
schemaExport.drop( false, true );
}
observer.sessionFactoryClosed( this );
eventListeners.destroyListeners();
}
public void evictEntity(String entityName, Serializable id) throws HibernateException {

View File

@ -0,0 +1,101 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.events;
import java.util.Set;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Configuration;
import org.hibernate.event.DeleteEventListener;
import org.hibernate.event.Destructible;
import org.hibernate.event.Initializable;
import org.hibernate.event.DeleteEvent;
import org.hibernate.junit.functional.FunctionalTestCase;
/**
* CallbackTest implementation
*
* @author Steve Ebersole
*/
public class CallbackTest extends FunctionalTestCase {
private TestingObserver observer = new TestingObserver();
private TestingListener listener = new TestingListener();
public CallbackTest(String string) {
super( string );
}
public String[] getMappings() {
return new String[0];
}
public void configure(Configuration cfg) {
cfg.setSessionFactoryObserver( observer );
cfg.getEventListeners().setDeleteEventListeners( new DeleteEventListener[] { listener } );
}
public void testCallbacks() {
assertTrue( "observer not notified of creation", observer.creationCount == 1 );
assertTrue( "listener not notified of creation", listener.initCount == 1 );
sfi().close();
assertTrue( "observer not notified of close", observer.closedCount == 1 );
assertTrue( "listener not notified of close", listener.destoryCount == 1 );
}
private static class TestingObserver implements SessionFactoryObserver {
private int creationCount = 0;
private int closedCount = 0;
public void sessionFactoryCreated(SessionFactory factory) {
creationCount++;
}
public void sessionFactoryClosed(SessionFactory factory) {
closedCount++;
}
}
private static class TestingListener implements DeleteEventListener, Initializable, Destructible {
private int initCount = 0;
private int destoryCount = 0;
public void initialize(Configuration cfg) {
initCount++;
}
public void cleanup() {
destoryCount++;
}
public void onDelete(DeleteEvent event) throws HibernateException {
}
public void onDelete(DeleteEvent event, Set transientEntities) throws HibernateException {
}
}
}