From 244623cce96646cfeea4b442e746ea62e9517790 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 28 Dec 2011 15:35:11 -0600 Subject: [PATCH] HHH-6822 - Split notions of (1) "naming" a SessionFactory and (2) specifying a JNDI name to which to bind it --- .../org/hibernate/cfg/AvailableSettings.java | 13 +- .../main/java/org/hibernate/cfg/Settings.java | 9 + .../org/hibernate/cfg/SettingsFactory.java | 7 +- .../internal/SessionFactoryImpl.java | 379 ++++++++++-------- .../internal/SessionFactoryRegistry.java | 54 ++- .../org/hibernate/jmx/SessionFactoryStub.java | 15 +- .../SessionFactorySerializationTest.java | 103 +++++ 7 files changed, 383 insertions(+), 197 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/serialization/SessionFactorySerializationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 9d99ef86a5..f4f1def079 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -28,10 +28,21 @@ package org.hibernate.cfg; */ public interface AvailableSettings { /** - * Names a {@literal JNDI} namespace into which the {@link org.hibernate.SessionFactory} should be bound. + * Defines a name for the {@link org.hibernate.SessionFactory}. Useful both to + * + * @see #SESSION_FACTORY_NAME_IS_JNDI */ public static final String SESSION_FACTORY_NAME = "hibernate.session_factory_name"; + /** + * Does the value defined by {@link #SESSION_FACTORY_NAME} represent a {@literal JNDI} namespace into which + * the {@link org.hibernate.SessionFactory} should be bound? + */ + public static final String SESSION_FACTORY_NAME_IS_JNDI = "hibernate.session_factory_name_is_jndi"; + /** * Names the {@link org.hibernate.service.jdbc.connections.spi.ConnectionProvider} to use for obtaining * JDBC connections. Can either reference an instance of diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java index 73b6047e98..0a155ae00a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Settings.java @@ -51,6 +51,7 @@ public final class Settings { private String defaultCatalogName; private Integer jdbcFetchSize; private String sessionFactoryName; + private boolean sessionFactoryNameAlsoJndiName; private boolean autoCreateSchema; private boolean autoDropSchema; private boolean autoUpdateSchema; @@ -147,6 +148,10 @@ public final class Settings { return sessionFactoryName; } + public boolean isSessionFactoryNameAlsoJndiName() { + return sessionFactoryNameAlsoJndiName; + } + public boolean isAutoCreateSchema() { return autoCreateSchema; } @@ -305,6 +310,10 @@ public final class Settings { sessionFactoryName = string; } + void setSessionFactoryNameAlsoJndiName(boolean sessionFactoryNameAlsoJndiName) { + this.sessionFactoryNameAlsoJndiName = sessionFactoryNameAlsoJndiName; + } + void setAutoCreateSchema(boolean b) { autoCreateSchema = b; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java index a98d453da6..97c197f799 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java @@ -73,8 +73,11 @@ public class SettingsFactory implements Serializable { //SessionFactory name: - String sessionFactoryName = props.getProperty(Environment.SESSION_FACTORY_NAME); - settings.setSessionFactoryName(sessionFactoryName); + String sessionFactoryName = props.getProperty( Environment.SESSION_FACTORY_NAME ); + settings.setSessionFactoryName( sessionFactoryName ); + settings.setSessionFactoryNameAlsoJndiName( + ConfigurationHelper.getBoolean( AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, props, true ) + ); //JDBC and connection settings: diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 035a892d4a..f0193426b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; import java.io.Serializable; import java.sql.Connection; import java.util.ArrayList; @@ -174,10 +173,10 @@ public final class SessionFactoryImpl private final String name; private final String uuid; - private final transient Map entityPersisters; + private final transient Map entityPersisters; private final transient Map classMetadata; - private final transient Map collectionPersisters; - private final transient Map collectionMetadata; + private final transient Map collectionPersisters; + private final transient Map collectionMetadata; private final transient Map> collectionRolesByEntityParticipant; private final transient Map identifierGenerators; private final transient Map namedQueries; @@ -208,7 +207,7 @@ public final class SessionFactoryImpl private final transient TransactionEnvironment transactionEnvironment; private final transient SessionFactoryOptions sessionFactoryOptions; - @SuppressWarnings( {"unchecked"} ) + @SuppressWarnings( {"unchecked", "ThrowableResultOfMethodCallIgnored"}) public SessionFactoryImpl( final Configuration cfg, Mapping mapping, @@ -355,7 +354,8 @@ public final class SessionFactoryImpl this.classMetadata = Collections.unmodifiableMap(classMeta); Map> tmpEntityToCollectionRoleMap = new HashMap>(); - collectionPersisters = new HashMap(); + collectionPersisters = new HashMap(); + Map tmpCollectionMetadata = new HashMap(); Iterator collections = cfg.getCollectionMappings(); while ( collections.hasNext() ) { Collection model = (Collection) collections.next(); @@ -378,7 +378,8 @@ public final class SessionFactoryImpl accessStrategy, this ) ; - collectionPersisters.put( model.getRole(), persister.getCollectionMetadata() ); + collectionPersisters.put( model.getRole(), persister ); + tmpCollectionMetadata.put( model.getRole(), persister.getCollectionMetadata() ); Type indexType = persister.getIndexType(); if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) { String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this ); @@ -400,7 +401,7 @@ public final class SessionFactoryImpl roles.add( persister.getRole() ); } } - collectionMetadata = Collections.unmodifiableMap(collectionPersisters); + collectionMetadata = Collections.unmodifiableMap( tmpCollectionMetadata ); Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator(); while ( itr.hasNext() ) { final Map.Entry entry = ( Map.Entry ) itr.next(); @@ -437,7 +438,13 @@ public final class SessionFactoryImpl catch (Exception e) { throw new AssertionFailure("Could not generate UUID"); } - SessionFactoryRegistry.INSTANCE.addSessionFactory( uuid, name, this, serviceRegistry.getService( JndiService.class ) ); + SessionFactoryRegistry.INSTANCE.addSessionFactory( + uuid, + name, + settings.isSessionFactoryNameAlsoJndiName(), + this, + serviceRegistry.getService( JndiService.class ) + ); LOG.debug( "Instantiated session factory" ); @@ -475,16 +482,14 @@ public final class SessionFactoryImpl //checking for named queries if ( settings.isNamedQueryStartupCheckingEnabled() ) { - Map errors = checkNamedQueries(); - if ( !errors.isEmpty() ) { - Set keys = errors.keySet(); - StringBuffer failingQueries = new StringBuffer( "Errors in named queries: " ); - for ( Iterator iterator = keys.iterator() ; iterator.hasNext() ; ) { - String queryName = ( String ) iterator.next(); - HibernateException e = ( HibernateException ) errors.get( queryName ); - failingQueries.append( queryName ); - if ( iterator.hasNext() ) failingQueries.append( ", " ); - LOG.namedQueryError( queryName, e ); + final Map errors = checkNamedQueries(); + if ( ! errors.isEmpty() ) { + StringBuilder failingQueries = new StringBuilder( "Errors in named queries: " ); + String sep = ""; + for ( Map.Entry entry : errors.entrySet() ) { + LOG.namedQueryError( entry.getKey(), entry.getValue() ); + failingQueries.append( entry.getKey() ).append( sep ); + sep = ", "; } throw new HibernateException( failingQueries.toString() ); } @@ -497,13 +502,12 @@ public final class SessionFactoryImpl final org.hibernate.mapping.FetchProfile mappingProfile = ( org.hibernate.mapping.FetchProfile ) itr.next(); final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() ); - Iterator fetches = mappingProfile.getFetches().iterator(); - while ( fetches.hasNext() ) { - final org.hibernate.mapping.FetchProfile.Fetch mappingFetch = - ( org.hibernate.mapping.FetchProfile.Fetch ) fetches.next(); + for ( org.hibernate.mapping.FetchProfile.Fetch mappingFetch : mappingProfile.getFetches() ) { // resolve the persister owning the fetch final String entityName = getImportedClassName( mappingFetch.getEntity() ); - final EntityPersister owner = ( EntityPersister ) ( entityName == null ? null : entityPersisters.get( entityName ) ); + final EntityPersister owner = entityName == null + ? null + : entityPersisters.get( entityName ); if ( owner == null ) { throw new HibernateException( "Unable to resolve entity reference [" + mappingFetch.getEntity() @@ -522,7 +526,7 @@ public final class SessionFactoryImpl // then construct the fetch instance... fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle ); - ( ( Loadable ) owner ).registerAffectingFetchProfile( fetchProfile.getName() ); + ((Loadable) owner).registerAffectingFetchProfile( fetchProfile.getName() ); } fetchProfiles.put( fetchProfile.getName(), fetchProfile ); } @@ -531,6 +535,7 @@ public final class SessionFactoryImpl this.observer.sessionFactoryCreated( this ); } + @SuppressWarnings( {"ThrowableResultOfMethodCallIgnored"}) public SessionFactoryImpl( MetadataImplementor metadata, SessionFactoryOptions sessionFactoryOptions, @@ -630,7 +635,7 @@ public final class SessionFactoryImpl } final String cacheRegionPrefix = stringBuilder.toString(); - entityPersisters = new HashMap(); + entityPersisters = new HashMap(); Map entityAccessStrategies = new HashMap(); Map classMeta = new HashMap(); for ( EntityBinding model : metadata.getEntityBindings() ) { @@ -667,7 +672,8 @@ public final class SessionFactoryImpl this.classMetadata = Collections.unmodifiableMap(classMeta); Map> tmpEntityToCollectionRoleMap = new HashMap>(); - collectionPersisters = new HashMap(); + collectionPersisters = new HashMap(); + Map tmpCollectionMetadata = new HashMap(); for ( PluralAttributeBinding model : metadata.getCollectionBindings() ) { if ( model.getAttribute() == null ) { throw new IllegalStateException( "No attribute defined for a AbstractPluralAttributeBinding: " + model ); @@ -694,13 +700,14 @@ public final class SessionFactoryImpl CollectionPersister persister = serviceRegistry .getService( PersisterFactory.class ) .createCollectionPersister( metadata, model, accessStrategy, this ); - collectionPersisters.put( model.getAttribute().getRole(), persister.getCollectionMetadata() ); + collectionPersisters.put( model.getAttribute().getRole(), persister ); + tmpCollectionMetadata.put( model.getAttribute().getRole(), persister.getCollectionMetadata() ); Type indexType = persister.getIndexType(); if ( indexType != null && indexType.isAssociationType() && !indexType.isAnyType() ) { String entityName = ( ( AssociationType ) indexType ).getAssociatedEntityName( this ); - Set roles = tmpEntityToCollectionRoleMap.get( entityName ); + Set roles = tmpEntityToCollectionRoleMap.get( entityName ); if ( roles == null ) { - roles = new HashSet(); + roles = new HashSet(); tmpEntityToCollectionRoleMap.put( entityName, roles ); } roles.add( persister.getRole() ); @@ -708,19 +715,17 @@ public final class SessionFactoryImpl Type elementType = persister.getElementType(); if ( elementType.isAssociationType() && !elementType.isAnyType() ) { String entityName = ( ( AssociationType ) elementType ).getAssociatedEntityName( this ); - Set roles = tmpEntityToCollectionRoleMap.get( entityName ); + Set roles = tmpEntityToCollectionRoleMap.get( entityName ); if ( roles == null ) { - roles = new HashSet(); + roles = new HashSet(); tmpEntityToCollectionRoleMap.put( entityName, roles ); } roles.add( persister.getRole() ); } } - collectionMetadata = Collections.unmodifiableMap(collectionPersisters); - Iterator itr = tmpEntityToCollectionRoleMap.entrySet().iterator(); - while ( itr.hasNext() ) { - final Map.Entry entry = ( Map.Entry ) itr.next(); - entry.setValue( Collections.unmodifiableSet( ( Set ) entry.getValue() ) ); + collectionMetadata = Collections.unmodifiableMap( tmpCollectionMetadata ); + for ( Map.Entry> entry : tmpEntityToCollectionRoleMap.entrySet() ) { + entry.setValue( Collections.unmodifiableSet( entry.getValue() ) ); } collectionRolesByEntityParticipant = Collections.unmodifiableMap( tmpEntityToCollectionRoleMap ); @@ -765,7 +770,13 @@ public final class SessionFactoryImpl catch (Exception e) { throw new AssertionFailure("Could not generate UUID"); } - SessionFactoryRegistry.INSTANCE.addSessionFactory( uuid, name, this, serviceRegistry.getService( JndiService.class ) ); + SessionFactoryRegistry.INSTANCE.addSessionFactory( + uuid, + name, + settings.isSessionFactoryNameAlsoJndiName(), + this, + serviceRegistry.getService( JndiService.class ) + ); LOG.debug("Instantiated session factory"); @@ -774,14 +785,7 @@ public final class SessionFactoryImpl .setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) ) .create( false, true ); } - /* - if ( settings.isAutoUpdateSchema() ) { - new SchemaUpdate( metadata ).execute( false, true ); - } - if ( settings.isAutoValidateSchema() ) { - new SchemaValidator( metadata ).validate(); - } - */ + if ( settings.isAutoDropSchema() ) { schemaExport = new SchemaExport( metadata ) .setImportSqlCommandExtractor( serviceRegistry.getService( ImportSqlCommandExtractor.class ) ); @@ -805,16 +809,14 @@ public final class SessionFactoryImpl //checking for named queries if ( settings.isNamedQueryStartupCheckingEnabled() ) { - Map errors = checkNamedQueries(); + final Map errors = checkNamedQueries(); if ( ! errors.isEmpty() ) { - Set keys = errors.keySet(); - StringBuffer failingQueries = new StringBuffer( "Errors in named queries: " ); - for ( Iterator iterator = keys.iterator() ; iterator.hasNext() ; ) { - String queryName = iterator.next(); - HibernateException e = ( HibernateException ) errors.get( queryName ); - failingQueries.append( queryName ); - if ( iterator.hasNext() ) failingQueries.append( ", " ); - LOG.namedQueryError( queryName, e ); + StringBuilder failingQueries = new StringBuilder( "Errors in named queries: " ); + String sep = ""; + for ( Map.Entry entry : errors.entrySet() ) { + LOG.namedQueryError( entry.getKey(), entry.getValue() ); + failingQueries.append( entry.getKey() ).append( sep ); + sep = ", "; } throw new HibernateException( failingQueries.toString() ); } @@ -827,7 +829,7 @@ public final class SessionFactoryImpl for ( org.hibernate.metamodel.binding.FetchProfile.Fetch mappingFetch : mappingProfile.getFetches() ) { // resolve the persister owning the fetch final String entityName = getImportedClassName( mappingFetch.getEntity() ); - final EntityPersister owner = ( EntityPersister ) ( entityName == null ? null : entityPersisters.get( entityName ) ); + final EntityPersister owner = entityName == null ? null : entityPersisters.get( entityName ); if ( owner == null ) { throw new HibernateException( "Unable to resolve entity reference [" + mappingFetch.getEntity() @@ -952,8 +954,9 @@ public final class SessionFactoryImpl return queryPlanCache; } - private Map checkNamedQueries() throws HibernateException { - Map errors = new HashMap(); + @SuppressWarnings( {"ThrowableResultOfMethodCallIgnored"}) + private Map checkNamedQueries() throws HibernateException { + Map errors = new HashMap(); // Check named HQL queries if ( LOG.isDebugEnabled() ) { @@ -1023,16 +1026,16 @@ public final class SessionFactoryImpl } public EntityPersister getEntityPersister(String entityName) throws MappingException { - EntityPersister result = (EntityPersister) entityPersisters.get(entityName); - if (result==null) { + EntityPersister result = entityPersisters.get(entityName); + if ( result == null ) { throw new MappingException( "Unknown entity: " + entityName ); } return result; } public CollectionPersister getCollectionPersister(String role) throws MappingException { - CollectionPersister result = (CollectionPersister) collectionPersisters.get(role); - if (result==null) { + CollectionPersister result = collectionPersisters.get(role); + if ( result == null ) { throw new MappingException( "Unknown collection role: " + role ); } return result; @@ -1086,25 +1089,6 @@ public final class SessionFactoryImpl ); } - private Object readResolve() throws ObjectStreamException { - LOG.trace( "Resolving serialized SessionFactory" ); - // look for the instance by uuid - Object result = SessionFactoryRegistry.INSTANCE.getSessionFactory( uuid ); - if ( result == null ) { - // in case we were deserialized in a different JVM, look for an instance with the same name - // (alternatively we could do an actual JNDI lookup here....) - result = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( name ); - if ( result == null ) { - throw new InvalidObjectException( "Could not find a SessionFactory [uuid=" + uuid + ",name=" + name + "]" ); - } - LOG.debug( "Resolved SessionFactory by name" ); - } - else { - LOG.debug( "Resolved SessionFactory by UUID" ); - } - return result; - } - public NamedQueryDefinition getNamedQuery(String queryName) { return namedQueries.get(queryName); } @@ -1124,18 +1108,6 @@ public final class SessionFactoryImpl return getEntityPersister(className).getIdentifierPropertyName(); } - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - LOG.trace( "Deserializing" ); - in.defaultReadObject(); - LOG.debugf( "Deserialized: %s", uuid ); - } - - private void writeObject(ObjectOutputStream out) throws IOException { - LOG.debugf( "Serializing: %s", uuid ); - out.defaultWriteObject(); - LOG.trace( "Serialized" ); - } - public Type[] getReturnTypes(String queryString) throws HibernateException { return queryPlanCache.getHQLQueryPlan( queryString, false, CollectionHelper.EMPTY_MAP ).getReturnMetadata().getReturnTypes(); } @@ -1149,15 +1121,19 @@ public final class SessionFactoryImpl } public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException { - return (CollectionMetadata) collectionMetadata.get(roleName); + return collectionMetadata.get(roleName); } public ClassMetadata getClassMetadata(String entityName) throws HibernateException { - return classMetadata.get(entityName); + return classMetadata.get( entityName ); } /** - * @param className + * Given the name of an entity class, determine all the class and interface names by which it can be + * referenced in an HQL query. + * + * @param className The name of the entity class + * * @return the names of all persistent (mapped) classes that extend or implement the * given class or interface, accounting for implicit/explicit polymorphism settings * and excluding mapped subclasses/joined-subclasses of other classes in the result. @@ -1173,44 +1149,42 @@ public final class SessionFactoryImpl return new String[] { className }; //for a dynamic-class } - ArrayList results = new ArrayList(); - Iterator iter = entityPersisters.values().iterator(); - while ( iter.hasNext() ) { - //test this entity to see if we must query it - EntityPersister testPersister = (EntityPersister) iter.next(); - if ( testPersister instanceof Queryable ) { - Queryable testQueryable = (Queryable) testPersister; - String testClassName = testQueryable.getEntityName(); - boolean isMappedClass = className.equals(testClassName); - if ( testQueryable.isExplicitPolymorphism() ) { - if ( isMappedClass ) { - return new String[] {className}; //NOTE EARLY EXIT - } + ArrayList results = new ArrayList(); + for ( EntityPersister checkPersister : entityPersisters.values() ) { + if ( ! Queryable.class.isInstance( checkPersister ) ) { + continue; + } + final Queryable checkQueryable = Queryable.class.cast( checkPersister ); + final String checkQueryableEntityName = checkQueryable.getEntityName(); + final boolean isMappedClass = className.equals( checkQueryableEntityName ); + if ( checkQueryable.isExplicitPolymorphism() ) { + if ( isMappedClass ) { + return new String[] { className }; //NOTE EARLY EXIT + } + } + else { + if ( isMappedClass ) { + results.add( checkQueryableEntityName ); } else { - if (isMappedClass) { - results.add(testClassName); - } - else { - final Class mappedClass = testQueryable.getMappedClass(); - if ( mappedClass!=null && clazz.isAssignableFrom( mappedClass ) ) { - final boolean assignableSuperclass; - if ( testQueryable.isInherited() ) { - Class mappedSuperclass = getEntityPersister( testQueryable.getMappedSuperclass() ).getMappedClass(); - assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass); - } - else { - assignableSuperclass = false; - } - if ( !assignableSuperclass ) { - results.add( testClassName ); - } + final Class mappedClass = checkQueryable.getMappedClass(); + if ( mappedClass != null && clazz.isAssignableFrom( mappedClass ) ) { + final boolean assignableSuperclass; + if ( checkQueryable.isInherited() ) { + Class mappedSuperclass = getEntityPersister( checkQueryable.getMappedSuperclass() ).getMappedClass(); + assignableSuperclass = clazz.isAssignableFrom( mappedSuperclass ); + } + else { + assignableSuperclass = false; + } + if ( !assignableSuperclass ) { + results.add( checkQueryableEntityName ); } } } } } - return (String[]) results.toArray( new String[ results.size() ] ); + return results.toArray( new String[ results.size() ] ); } public String getImportedClassName(String className) { @@ -1306,7 +1280,10 @@ public final class SessionFactoryImpl } SessionFactoryRegistry.INSTANCE.removeSessionFactory( - uuid, name, serviceRegistry.getService( JndiService.class ) + uuid, + name, + settings.isSessionFactoryNameAlsoJndiName(), + serviceRegistry.getService( JndiService.class ) ); observer.sessionFactoryClosed( this ); @@ -1364,9 +1341,8 @@ public final class SessionFactoryImpl } public void evictEntityRegions() { - Iterator entityNames = entityPersisters.keySet().iterator(); - while ( entityNames.hasNext() ) { - evictEntityRegion( ( String ) entityNames.next() ); + for ( String s : entityPersisters.keySet() ) { + evictEntityRegion( s ); } } @@ -1409,9 +1385,8 @@ public final class SessionFactoryImpl } public void evictCollectionRegions() { - Iterator collectionRoles = collectionPersisters.keySet().iterator(); - while ( collectionRoles.hasNext() ) { - evictCollectionRegion( ( String ) collectionRoles.next() ); + for ( String s : collectionPersisters.keySet() ) { + evictCollectionRegion( s ); } } @@ -1521,6 +1496,7 @@ public final class SessionFactoryImpl return allCacheRegions.get( regionName ); } + @SuppressWarnings( {"unchecked"}) public Map getAllSecondLevelCacheRegions() { return new HashMap( allCacheRegions ); } @@ -1623,52 +1599,13 @@ public final class SessionFactoryImpl } public FetchProfile getFetchProfile(String name) { - return ( FetchProfile ) fetchProfiles.get( name ); + return fetchProfiles.get( name ); } public TypeHelper getTypeHelper() { return typeHelper; } - /** - * Custom serialization hook used during Session serialization. - * - * @param oos The stream to which to write the factory - * @throws IOException Indicates problems writing out the serial data stream - */ - void serialize(ObjectOutputStream oos) throws IOException { - oos.writeUTF( uuid ); - oos.writeBoolean( name != null ); - if ( name != null ) { - oos.writeUTF( name ); - } - } - - /** - * Custom deserialization hook used during Session deserialization. - * - * @param ois The stream from which to "read" the factory - * @return The deserialized factory - * @throws IOException indicates problems reading back serial data stream - * @throws ClassNotFoundException indicates problems reading back serial data stream - */ - static SessionFactoryImpl deserialize(ObjectInputStream ois) throws IOException, ClassNotFoundException { - final String uuid = ois.readUTF(); - boolean isNamed = ois.readBoolean(); - final String name = isNamed ? ois.readUTF() : null; - Object result = SessionFactoryRegistry.INSTANCE.getSessionFactory( uuid ); - if ( result == null ) { - LOG.tracev( "Could not locate session factory by uuid [{0}] during session deserialization; trying name", uuid ); - if ( isNamed ) { - result = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( name ); - } - if ( result == null ) { - throw new InvalidObjectException( "could not resolve session factory during session deserialization [uuid=" + uuid + ", name=" + name + "]" ); - } - } - return ( SessionFactoryImpl ) result; - } - static class SessionBuilderImpl implements SessionBuilder { private final SessionFactoryImpl sessionFactory; private Interceptor interceptor; @@ -1785,4 +1722,98 @@ public final class SessionFactoryImpl return this; } } + + + // Serialization handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * Custom serialization hook defined by Java spec. Used when the factory is directly serialized + * + * @param out The stream into which the object is being serialized. + * + * @throws IOException Can be thrown by the stream + */ + private void writeObject(ObjectOutputStream out) throws IOException { + LOG.debugf( "Serializing: %s", uuid ); + out.defaultWriteObject(); + LOG.trace( "Serialized" ); + } + + /** + * Custom serialization hook defined by Java spec. Used when the factory is directly deserialized + * + * @param in The stream from which the object is being deserialized. + * + * @throws IOException Can be thrown by the stream + * @throws ClassNotFoundException Again, can be thrown by the stream + */ + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + LOG.trace( "Deserializing" ); + in.defaultReadObject(); + LOG.debugf( "Deserialized: %s", uuid ); + } + + /** + * Custom serialization hook defined by Java spec. Used when the factory is directly deserialized. + * Here we resolve the uuid/name read from the stream previously to resolve the SessionFactory + * instance to use based on the registrations with the {@link SessionFactoryRegistry} + * + * @return The resolved factory to use. + * + * @throws InvalidObjectException Thrown if we could not resolve the factory by uuid/name. + */ + private Object readResolve() throws InvalidObjectException { + LOG.trace( "Resolving serialized SessionFactory" ); + return locateSessionFactoryOnDeserialization( uuid, name ); + } + + private static SessionFactory locateSessionFactoryOnDeserialization(String uuid, String name) throws InvalidObjectException{ + final SessionFactory uuidResult = SessionFactoryRegistry.INSTANCE.getSessionFactory( uuid ); + if ( uuidResult != null ) { + LOG.debugf( "Resolved SessionFactory by UUID [%s]", uuid ); + return uuidResult; + } + + // in case we were deserialized in a different JVM, look for an instance with the same name + // (provided we were given a name) + if ( name != null ) { + final SessionFactory namedResult = SessionFactoryRegistry.INSTANCE.getNamedSessionFactory( name ); + if ( namedResult != null ) { + LOG.debugf( "Resolved SessionFactory by name [%s]", name ); + return namedResult; + } + } + + throw new InvalidObjectException( "Could not find a SessionFactory [uuid=" + uuid + ",name=" + name + "]" ); + } + + /** + * Custom serialization hook used during Session serialization. + * + * @param oos The stream to which to write the factory + * @throws IOException Indicates problems writing out the serial data stream + */ + void serialize(ObjectOutputStream oos) throws IOException { + oos.writeUTF( uuid ); + oos.writeBoolean( name != null ); + if ( name != null ) { + oos.writeUTF( name ); + } + } + + /** + * Custom deserialization hook used during Session deserialization. + * + * @param ois The stream from which to "read" the factory + * @return The deserialized factory + * @throws IOException indicates problems reading back serial data stream + * @throws ClassNotFoundException indicates problems reading back serial data stream + */ + static SessionFactoryImpl deserialize(ObjectInputStream ois) throws IOException, ClassNotFoundException { + LOG.trace( "Deserializing SessionFactory from Session" ); + final String uuid = ois.readUTF(); + boolean isNamed = ois.readBoolean(); + final String name = isNamed ? ois.readUTF() : null; + return (SessionFactoryImpl) locateSessionFactoryOnDeserialization( uuid, name ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryRegistry.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryRegistry.java index 7d3f115457..41dcfdeb71 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryRegistry.java @@ -62,18 +62,28 @@ public class SessionFactoryRegistry { LOG.debugf( "Initializing SessionFactoryRegistry : %s", this ); } - public void addSessionFactory(String uuid, String name, SessionFactory instance, JndiService jndiService) { + public void addSessionFactory( + String uuid, + String name, + boolean isNameAlsoJndiName, + SessionFactory instance, + JndiService jndiService) { + if ( uuid == null ) { + throw new IllegalArgumentException( "SessionFactory UUID cannot be null" ); + } + LOG.debugf( "Registering SessionFactory: %s (%s)", uuid, name == null ? "" : name ); sessionFactoryMap.put( uuid, instance ); + if ( name != null ) { + nameUuidXref.put( name, uuid ); + } - if ( name == null ) { - LOG.debug( "Not binding factory to JNDI, no JNDI name configured" ); + if ( name == null || ! isNameAlsoJndiName ) { + LOG.debug( "Not binding SessionFactory to JNDI, no JNDI name configured" ); return; } - nameUuidXref.put( name, uuid ); - - LOG.debugf( "SessionFactory name : %s, attempting to bind to JNDI", name ); + LOG.debugf( "Attempting to bind SessionFactory [%s] to JNDI", name ); try { jndiService.bind( name, instance ); @@ -93,21 +103,27 @@ public class SessionFactoryRegistry { } } - public void removeSessionFactory(String uuid, String name, JndiService jndiService) { + public void removeSessionFactory( + String uuid, + String name, + boolean isNameAlsoJndiName, + JndiService jndiService) { if ( name != null ) { - try { - LOG.tracef( "Unbinding SessionFactory from JNDI : %s", name ); - jndiService.unbind( name ); - LOG.factoryUnboundFromJndiName( name ); - } - catch ( JndiNameException e ) { - LOG.invalidJndiName( name, e ); - } - catch ( JndiException e ) { - LOG.unableToUnbindFactoryFromJndi( e ); - } - nameUuidXref.remove( name ); + + if ( isNameAlsoJndiName ) { + try { + LOG.tracef( "Unbinding SessionFactory from JNDI : %s", name ); + jndiService.unbind( name ); + LOG.factoryUnboundFromJndiName( name ); + } + catch ( JndiNameException e ) { + LOG.invalidJndiName( name, e ); + } + catch ( JndiException e ) { + LOG.unableToUnbindFactoryFromJndi( e ); + } + } } sessionFactoryMap.remove( uuid ); diff --git a/hibernate-core/src/main/java/org/hibernate/jmx/SessionFactoryStub.java b/hibernate-core/src/main/java/org/hibernate/jmx/SessionFactoryStub.java index c0738d9b6c..837aa39be0 100644 --- a/hibernate-core/src/main/java/org/hibernate/jmx/SessionFactoryStub.java +++ b/hibernate-core/src/main/java/org/hibernate/jmx/SessionFactoryStub.java @@ -44,11 +44,13 @@ import org.hibernate.SessionFactory; import org.hibernate.StatelessSession; import org.hibernate.StatelessSessionBuilder; import org.hibernate.TypeHelper; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.UUIDGenerator; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.SessionFactoryRegistry; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.CollectionMetadata; import org.hibernate.service.jndi.internal.JndiServiceImpl; @@ -65,6 +67,7 @@ import org.hibernate.stat.Statistics; * @deprecated See HHH-6190 for details */ @Deprecated +@SuppressWarnings( {"deprecation"}) public class SessionFactoryStub implements SessionFactory { private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator(); @@ -85,7 +88,17 @@ public class SessionFactoryStub implements SessionFactory { throw new AssertionFailure("Could not generate UUID"); } - SessionFactoryRegistry.INSTANCE.addSessionFactory( uuid, name, this, new JndiServiceImpl( service.getProperties() ) ); + SessionFactoryRegistry.INSTANCE.addSessionFactory( + uuid, + name, + ConfigurationHelper.getBoolean( + AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, + service.getProperties(), + true + ), + this, + new JndiServiceImpl( service.getProperties() ) + ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/serialization/SessionFactorySerializationTest.java b/hibernate-core/src/test/java/org/hibernate/serialization/SessionFactorySerializationTest.java new file mode 100644 index 0000000000..6c4fdeffda --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/serialization/SessionFactorySerializationTest.java @@ -0,0 +1,103 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2011, Red Hat Inc. 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 Inc. + * + * 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.serialization; + + +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.StringRefAddr; + +import java.io.InvalidObjectException; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; +import org.hibernate.id.UUIDGenerator; +import org.hibernate.internal.SessionFactoryRegistry; +import org.hibernate.internal.util.SerializationHelper; +import org.hibernate.type.SerializationException; + +import org.junit.Test; + +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +/** + * @author Steve Ebersole + */ +public class SessionFactorySerializationTest extends BaseUnitTestCase { + public static final String NAME = "mySF"; + + @Test + public void testNamedSessionFactorySerialization() throws Exception { + Configuration cfg = new Configuration() + .setProperty( AvailableSettings.SESSION_FACTORY_NAME, NAME ) + .setProperty( AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, "false" ); // default is true + SessionFactory factory = cfg.buildSessionFactory(); + + // we need to do some tricking here so that Hibernate thinks the deserialization happens in a + // different VM + Reference reference = factory.getReference(); + StringRefAddr refAddr = (StringRefAddr) reference.get( "uuid" ); + String uuid = (String) refAddr.getContent(); + // deregister under this uuid... + SessionFactoryRegistry.INSTANCE.removeSessionFactory( uuid, NAME, false, null ); + // and then register under a different uuid... + SessionFactoryRegistry.INSTANCE.addSessionFactory( "some-other-uuid", NAME, false, factory, null ); + + SessionFactory factory2 = (SessionFactory) SerializationHelper.clone( factory ); + assertSame( factory, factory2 ); + factory.close(); + } + + @Test + public void testUnNamedSessionFactorySerialization() throws Exception { + // IMPL NOTE : this test is a control to testNamedSessionFactorySerialization + // here, the test should fail based just on attempted uuid resolution + Configuration cfg = new Configuration() + .setProperty( AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, "false" ); // default is true + SessionFactory factory = cfg.buildSessionFactory(); + + // we need to do some tricking here so that Hibernate thinks the deserialization happens in a + // different VM + Reference reference = factory.getReference(); + StringRefAddr refAddr = (StringRefAddr) reference.get( "uuid" ); + String uuid = (String) refAddr.getContent(); + // deregister under this uuid... + SessionFactoryRegistry.INSTANCE.removeSessionFactory( uuid, NAME, false, null ); + // and then register under a different uuid... + SessionFactoryRegistry.INSTANCE.addSessionFactory( "some-other-uuid", NAME, false, factory, null ); + + try { + SerializationHelper.clone( factory ); + fail( "Expecting an error" ); + } + catch ( SerializationException expected ) { + + } + factory.close(); + } +}