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

View File

@ -23,7 +23,6 @@ package org.hibernate.ejb;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
@ -31,6 +30,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
@ -49,6 +49,8 @@ import org.hibernate.ejb.internal.EntityManagerFactoryRegistry;
import org.hibernate.ejb.metamodel.MetamodelImpl;
import org.hibernate.ejb.util.PersistenceUtilHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.UUIDGenerator;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.service.ServiceRegistry;
@ -61,17 +63,19 @@ import org.hibernate.service.ServiceRegistry;
* @author Steve Ebersole
*/
public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
private final SessionFactory sessionFactory;
private final PersistenceUnitTransactionType transactionType;
private final boolean discardOnClose;
private final Class sessionInterceptorClass;
private final CriteriaBuilderImpl criteriaBuilder;
private final Metamodel metamodel;
private final HibernatePersistenceUnitUtil util;
private final Map<String,Object> properties;
private static final long serialVersionUID = 5423543L;
private static final IdentifierGenerator UUID_GENERATOR = UUIDGenerator.buildSessionFactoryUniqueIdentifierGenerator();
private final transient SessionFactory sessionFactory;
private final transient PersistenceUnitTransactionType transactionType;
private final transient boolean discardOnClose;
private final transient Class sessionInterceptorClass;
private final transient CriteriaBuilderImpl criteriaBuilder;
private final transient Metamodel metamodel;
private final transient HibernatePersistenceUnitUtil util;
private final transient Map<String,Object> properties;
private final String entityManagerFactoryName;
private final PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache();
private final transient PersistenceUtilHelper.MetadataCache cache = new PersistenceUtilHelper.MetadataCache();
@SuppressWarnings( "unchecked" )
public EntityManagerFactoryImpl(
@ -104,6 +108,9 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
if (entityManagerFactoryName == null) {
entityManagerFactoryName = persistenceUnitName;
}
if (entityManagerFactoryName == null) {
entityManagerFactoryName = (String) UUID_GENERATOR.generate(null, null);
}
this.entityManagerFactoryName = entityManagerFactoryName;
EntityManagerFactoryRegistry.INSTANCE.addEntityManagerFactory(entityManagerFactoryName, this);
}
@ -199,29 +206,7 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
}
}
/**
* 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();
private static EntityManagerFactory getNamedEntityManagerFactory(String entityManagerFactoryName) throws InvalidObjectException {
Object result = EntityManagerFactoryRegistry.INSTANCE.getNamedEntityManagerFactory(entityManagerFactoryName);
if ( result == null ) {
@ -231,6 +216,25 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
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 final HibernateEntityManagerFactory emf;

View File

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

View File

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

View File

@ -23,6 +23,8 @@
*/
package org.hibernate.ejb.test.ejb3configuration;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
@ -104,6 +106,27 @@ public class EntityManagerFactorySerializationTest extends BaseEntityManagerFunc
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
public Class[] getAnnotatedClasses() {
return new Class[]{