HHH-7018 EntityManagerFactory should be serializable even if it references nonserializable objects and also changed the HHH-6897 fix to use a UUID for emf name

This commit is contained in:
Scott Marlow 2012-02-06 16:41:00 -05:00
parent edda5ec7f4
commit 608c2fadc8
5 changed files with 78 additions and 52 deletions

View File

@ -23,6 +23,16 @@
*/ */
package org.hibernate.ejb; package org.hibernate.ejb;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.CacheRetrieveMode; import javax.persistence.CacheRetrieveMode;
import javax.persistence.CacheStoreMode; import javax.persistence.CacheStoreMode;
import javax.persistence.EntityExistsException; import javax.persistence.EntityExistsException;
@ -53,16 +63,6 @@ import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.transaction.Status; import javax.transaction.Status;
import javax.transaction.SystemException; import javax.transaction.SystemException;
import javax.transaction.TransactionManager; import javax.transaction.TransactionManager;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
@ -124,6 +124,7 @@ import org.jboss.logging.Logger;
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public abstract class AbstractEntityManagerImpl implements HibernateEntityManagerImplementor, Serializable { public abstract class AbstractEntityManagerImpl implements HibernateEntityManagerImplementor, Serializable {
private static final long serialVersionUID = 78818181L;
private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class,
AbstractEntityManagerImpl.class.getName()); AbstractEntityManagerImpl.class.getName());
@ -139,7 +140,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
entityManagerSpecificProperties.add( QueryHints.SPEC_HINT_TIMEOUT ); entityManagerSpecificProperties.add( QueryHints.SPEC_HINT_TIMEOUT );
} }
private transient EntityManagerFactoryImpl entityManagerFactory; private EntityManagerFactoryImpl entityManagerFactory;
protected transient TransactionImpl tx = new TransactionImpl( this ); protected transient TransactionImpl tx = new TransactionImpl( this );
protected PersistenceContextType persistenceContextType; protected PersistenceContextType persistenceContextType;
private PersistenceUnitTransactionType transactionType; private PersistenceUnitTransactionType transactionType;
@ -1238,12 +1239,10 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
private void writeObject(ObjectOutputStream oos) throws IOException { private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); oos.defaultWriteObject();
entityManagerFactory.serialize(oos);
} }
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject(); ois.defaultReadObject();
entityManagerFactory = (EntityManagerFactoryImpl)EntityManagerFactoryImpl.deserialize(ois);
tx = new TransactionImpl( this ); tx = new TransactionImpl( this );
} }

View File

@ -23,7 +23,6 @@ package org.hibernate.ejb;
import java.io.IOException; import java.io.IOException;
import java.io.InvalidObjectException; import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
@ -31,6 +30,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.persistence.Cache; import javax.persistence.Cache;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
@ -49,6 +49,8 @@ import org.hibernate.ejb.internal.EntityManagerFactoryRegistry;
import org.hibernate.ejb.metamodel.MetamodelImpl; import org.hibernate.ejb.metamodel.MetamodelImpl;
import org.hibernate.ejb.util.PersistenceUtilHelper; import org.hibernate.ejb.util.PersistenceUtilHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerator;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata; import org.hibernate.metadata.ClassMetadata;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
@ -61,17 +63,19 @@ import org.hibernate.service.ServiceRegistry;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory { public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
private final SessionFactory sessionFactory; private static final long serialVersionUID = 5423543L;
private final PersistenceUnitTransactionType transactionType; private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
private final boolean discardOnClose; private final transient SessionFactory sessionFactory;
private final Class sessionInterceptorClass; private final transient PersistenceUnitTransactionType transactionType;
private final CriteriaBuilderImpl criteriaBuilder; private final transient boolean discardOnClose;
private final Metamodel metamodel; private final transient Class sessionInterceptorClass;
private final HibernatePersistenceUnitUtil util; private final transient CriteriaBuilderImpl criteriaBuilder;
private final Map<String,Object> properties; private final transient Metamodel metamodel;
private final transient HibernatePersistenceUnitUtil util;
private final transient Map<String,Object> properties;
private final String entityManagerFactoryName; private final String entityManagerFactoryName;
private final PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache(); private final transient PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache();
@SuppressWarnings( "unchecked" ) @SuppressWarnings( "unchecked" )
public EntityManagerFactoryImpl( public EntityManagerFactoryImpl(
@ -104,6 +108,9 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
if (entityManagerFactoryName == null) { if (entityManagerFactoryName == null) {
entityManagerFactoryName = persistenceUnitName; entityManagerFactoryName = persistenceUnitName;
} }
if (entityManagerFactoryName == null) {
entityManagerFactoryName = (String) UUID_GENERATOR.generate(null, null);
}
this.entityManagerFactoryName = entityManagerFactoryName; this.entityManagerFactoryName = entityManagerFactoryName;
EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory(entityManagerFactoryName, this); EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory(entityManagerFactoryName, this);
} }
@ -199,29 +206,7 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
} }
} }
/** private static EntityManagerFactory getNamedEntityManagerFactory(String entityManagerFactoryName) throws InvalidObjectException {
* Custom serialization hook used during EntityManager 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 {
if (entityManagerFactoryName == null) {
throw new InvalidObjectException( "could not serialize entity manager factory with null entityManagerFactoryName" );
}
oos.writeUTF( entityManagerFactoryName );
}
/**
* Custom deserialization hook used during EntityManager 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 EntityManagerFactory deserialize(ObjectInputStream ois) throws IOException, ClassNotFoundException {
final String entityManagerFactoryName = ois.readUTF();
Object result = EntityManagerFactoryRegistry.INSTANCE.getNamedEntityManagerFactory(entityManagerFactoryName); Object result = EntityManagerFactoryRegistry.INSTANCE.getNamedEntityManagerFactory(entityManagerFactoryName);
if ( result == null ) { if ( result == null ) {
@ -231,6 +216,25 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
return (EntityManagerFactory)result; return (EntityManagerFactory)result;
} }
private void writeObject(ObjectOutputStream oos) throws IOException {
if (entityManagerFactoryName == null) {
throw new InvalidObjectException( "could not serialize entity manager factory with null entityManagerFactoryName" );
}
oos.defaultWriteObject();
}
/**
* After deserialization of an EntityManagerFactory, this is invoked to return the EntityManagerFactory instance
* that is already in use rather than a cloned copy of the object.
*
* @return
* @throws InvalidObjectException
*/
private Object readResolve() throws InvalidObjectException {
return getNamedEntityManagerFactory(entityManagerFactoryName);
}
private static class HibernatePersistenceUnitUtil implements PersistenceUnitUtil, Serializable { private static class HibernatePersistenceUnitUtil implements PersistenceUnitUtil, Serializable {
private final HibernateEntityManagerFactory emf; private final HibernateEntityManagerFactory emf;

View File

@ -71,15 +71,16 @@ public class EntityManagerFactoryRegistry {
entityManagerFactorySet.add(entityManagerFactory); entityManagerFactorySet.add(entityManagerFactory);
Set<EntityManagerFactory> previous = entityManagerFactoryMap.putIfAbsent( name, entityManagerFactorySet); Set<EntityManagerFactory> previous = entityManagerFactoryMap.putIfAbsent( name, entityManagerFactorySet);
// if multiple entries are found, give warning and add EMF to existing set // if already added under 'name'. Where 'name' could be session factory name, pu name or uuid (previous
// later, if EntityManagerFactoryImpl.deserialize is called for an EM returned from any EMF in the set // will be null). We will give a warning that an EntityManagerFactory is created with the same name
// an exception will be thrown (failing the deserialization attempt). // as is already used for a different EMF. The best way to avoid the warning is to specify the AvailableSettings.SESSION_FACTORY_NAME
// with a unique name.
if (previous != null) { if (previous != null) {
LOG.entityManagerFactoryAlreadyRegistered(name, AvailableSettings.ENTITY_MANAGER_FACTORY_NAME); LOG.entityManagerFactoryAlreadyRegistered(name, AvailableSettings.ENTITY_MANAGER_FACTORY_NAME);
boolean done = false; boolean done = false;
while( !done) { while( !done) {
synchronized (previous) { synchronized (previous) {
if (entityManagerFactoryMap.get(name) == previous) { // compare and add EMF if same if (entityManagerFactoryMap.get(name) == previous) { // compare and set EMF if same
previous.add(entityManagerFactory); previous.add(entityManagerFactory);
done = true; done = true;
} }

View File

@ -109,8 +109,7 @@ public abstract class BaseEntityManagerFunctionalTestCase extends BaseUnitTestCa
} }
ejb3Configuration ejb3Configuration
.getHibernateConfiguration() .getHibernateConfiguration()
.setProperty( org.hibernate.cfg.AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true") .setProperty( org.hibernate.cfg.AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true");
.setProperty( AvailableSettings.ENTITY_MANAGER_FACTORY_NAME, "EMF_BaseEntityManagerFunctionalTestCase");
ejb3Configuration ejb3Configuration
.getHibernateConfiguration() .getHibernateConfiguration()

View File

@ -23,6 +23,8 @@
*/ */
package org.hibernate.ejb.test.ejb3configuration; package org.hibernate.ejb.test.ejb3configuration;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
@ -104,6 +106,27 @@ public class EntityManagerFactorySerializationTest extends BaseEntityManagerFunc
em.close(); em.close();
} }
@Test
public void testEntityManagerFactorySerialization() throws Exception {
EntityManagerFactory entityManagerFactory = entityManagerFactory();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream( stream );
out.writeObject( entityManagerFactory );
out.close();
byte[] serialized = stream.toByteArray();
stream.close();
ByteArrayInputStream byteIn = new ByteArrayInputStream( serialized );
ObjectInputStream in = new ObjectInputStream( byteIn );
EntityManagerFactory entityManagerFactory2 = (EntityManagerFactory) in.readObject();
in.close();
byteIn.close();
assertTrue("deserialized EntityManagerFactory should be the same original EntityManagerFactory instance",
entityManagerFactory2 == entityManagerFactory);
}
@Override @Override
public Class[] getAnnotatedClasses() { public Class[] getAnnotatedClasses() {
return new Class[]{ return new Class[]{