HHH-7573 : Re-work test case to be similar to org.hibernate.test.instrument tests

This commit is contained in:
Gail Badner 2015-07-28 15:38:32 -07:00
parent 8653c3195e
commit 0b6ea757e3
8 changed files with 447 additions and 108 deletions

View File

@ -112,17 +112,3 @@ task testJar(type: Jar, dependsOn: testClasses) {
artifacts {
tests testJar
}
task instrument(dependsOn: compileTestJava) << {
ant.taskdef(name: 'hibInstrument',
classname: 'org.hibernate.tool.instrument.javassist.InstrumentTask',
classpath: configurations.testCompile.asPath)
ant.hibInstrument(verbose: 'true'){
fileset(dir:"$buildDir/classes/test/org/hibernate/jpa/test/callbacks/"){
include(name: "EntityWithLazyProperty.class")
}
}
println("hibernate instrumentation")
}
test.dependsOn instrument

View File

@ -13,14 +13,11 @@ import javax.persistence.EntityManager;
import org.junit.Test;
import org.hibernate.Hibernate;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.jpa.test.Cat;
import org.hibernate.jpa.test.Kitten;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.TestForIssue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -254,96 +251,7 @@ public class CallbacksTest extends BaseEntityManagerFunctionalTestCase {
RemoteControl.class,
Rythm.class,
Plant.class,
Kitten.class,
EntityWithLazyProperty.class
Kitten.class
};
}
/**
* Test for HHH-7573.
* Load some test data into an entity which has a lazy property and a @PreUpdate callback, then reload and update a
* non lazy field which will trigger the PreUpdate lifecycle callback.
* @throws Exception
*/
@Test
@TestForIssue( jiraKey = "HHH-7573" )
public void testJpaFlushEntityEventListener() throws Exception {
EntityWithLazyProperty entity;
EntityManager em = getOrCreateEntityManager();
byte[] testArray = new byte[]{0x2A};
//persist the test entity.
em.getTransaction().begin();
entity = new EntityWithLazyProperty();
entity.setSomeField("TEST");
entity.setLazyData(testArray);
em.persist(entity);
em.getTransaction().commit();
em.close();
checkLazyField(entity, em, testArray);
/**
* Set a non lazy field, therefore the lazyData field will be LazyPropertyInitializer.UNFETCHED_PROPERTY
* for both state and newState so the field should not change. This should no longer cause a ClassCastException.
*/
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
entity.setSomeField("TEST1");
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.getTransaction().commit();
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.close();
checkLazyField(entity, em, testArray);
/**
* Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
* PreUpdate annotated callback method. So state == LazyPropertyInitializer.UNFETCHED_PROPERTY and
* newState == EntityWithLazyProperty.PRE_UPDATE_VALUE. This should no longer cause a ClassCastException.
*/
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
entity.setUpdateLazyFieldInPreUpdate(true);
entity.setSomeField("TEST2");
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.getTransaction().commit();
assertTrue( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.close();
checkLazyField(entity, em, EntityWithLazyProperty.PRE_UPDATE_VALUE);
/**
* Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
* PreUpdate annotated callback method and also set the lazyData field directly to testArray1. When we reload we
* should get EntityWithLazyProperty.PRE_UPDATE_VALUE.
*/
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
entity.setUpdateLazyFieldInPreUpdate(true);
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
entity.setLazyData(testArray);
assertTrue( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
entity.setSomeField("TEST3");
em.getTransaction().commit();
em.close();
checkLazyField( entity, em, EntityWithLazyProperty.PRE_UPDATE_VALUE);
}
private void checkLazyField(EntityWithLazyProperty entity, EntityManager em, byte[] expected) {
// reload the entity and check the lazy value matches what we expect.
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData") );
assertTrue( ArrayHelper.isEquals( expected, entity.getLazyData() ) );
assertTrue( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.getTransaction().commit();
em.close();
}
}

View File

@ -0,0 +1,181 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.instrument.cases;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.hibernate.bytecode.spi.InstrumentedClassLoader;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.HibernateEntityManagerFactory;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
/**
* @author Steve Ebersole
* @author Gail Badner
*/
public abstract class AbstractExecutable implements Executable {
private static final Dialect dialect = Dialect.getDialect();
private HibernateEntityManagerFactory entityManagerFactory;
private EntityManager em;
@Override
public final void prepare() {
// make sure we pick up the TCCL, and make sure its the isolated CL...
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if ( classLoader == null ) {
throw new RuntimeException( "Isolated ClassLoader not yet set as TCCL" );
}
if ( !InstrumentedClassLoader.class.isInstance( classLoader ) ) {
throw new RuntimeException( "Isolated ClassLoader not yet set as TCCL" );
}
entityManagerFactory = Bootstrap.getEntityManagerFactoryBuilder(
buildPersistenceUnitDescriptor( getClass().getSimpleName() ),
buildSettings(),
classLoader
).build().unwrap( HibernateEntityManagerFactory.class );
}
@Override
public final void complete() {
try {
cleanup();
}
finally {
if ( em != null && em.isOpen() ) {
em.close();
}
em = null;
entityManagerFactory.close();
entityManagerFactory = null;
}
}
protected EntityManager getOrCreateEntityManager() {
if ( em == null || !em.isOpen() ) {
em = entityManagerFactory.createEntityManager();
}
return em;
}
protected void cleanup() {
}
private Map buildSettings() {
Map<Object, Object> settings = Environment.getProperties();
ArrayList<Class> classes = new ArrayList<Class>();
classes.addAll( Arrays.asList( getAnnotatedClasses() ) );
settings.put( AvailableSettings.LOADED_CLASSES, classes );
settings.put( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "create-drop" );
settings.put( org.hibernate.cfg.AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true" );
settings.put( org.hibernate.cfg.AvailableSettings.DIALECT, dialect.getClass().getName() );
return settings;
}
private PersistenceUnitDescriptor buildPersistenceUnitDescriptor(final String puName) {
return new PersistenceUnitDescriptor() {
private final String name = puName;
@Override public URL getPersistenceUnitRootUrl() {
return null;
}
@Override
public String getName() {
return name;
}
@Override
public String getProviderClassName() {
return HibernatePersistenceProvider.class.getName();
}
@Override
public boolean isUseQuotedIdentifiers() {
return false;
}
@Override
public boolean isExcludeUnlistedClasses() {
return false;
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return null;
}
@Override
public ValidationMode getValidationMode() {
return null;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
@Override
public List<String> getManagedClassNames() {
return null;
}
@Override
public List<String> getMappingFileNames() {
return null;
}
@Override
public List<URL> getJarFileUrls() {
return null;
}
@Override
public Object getNonJtaDataSource() {
return null;
}
@Override
public Object getJtaDataSource() {
return null;
}
@Override
public Properties getProperties() {
return null;
}
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public ClassLoader getTempClassLoader() {
return null;
}
@Override
public void pushClassTransformer(Collection<String> entityClassNames) {
}
};
}
}

View File

@ -0,0 +1,19 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.instrument.cases;
import java.util.Map;
/**
* @author Steve Ebersole
*/
public interface Executable {
public void prepare();
public void execute() throws Exception;
public void complete();
public Class[] getAnnotatedClasses();
}

View File

@ -0,0 +1,114 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.instrument.cases;
import javax.persistence.EntityManager;
import org.junit.Test;
import org.hibernate.Hibernate;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.jpa.test.instrument.domain.EntityWithLazyProperty;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
*
*/
public class TestLazyPropertyOnPreUpdateExecutable extends AbstractExecutable {
@Override
public void execute() throws Exception {
EntityWithLazyProperty entity;
EntityManager em = getOrCreateEntityManager();
byte[] testArray = new byte[]{0x2A};
//persist the test entity.
em.getTransaction().begin();
entity = new EntityWithLazyProperty();
entity.setSomeField("TEST");
entity.setLazyData(testArray);
em.persist(entity);
em.getTransaction().commit();
em.close();
checkLazyField(entity, em, testArray);
/**
* Set a non lazy field, therefore the lazyData field will be LazyPropertyInitializer.UNFETCHED_PROPERTY
* for both state and newState so the field should not change. This should no longer cause a ClassCastException.
*/
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
entity.setSomeField("TEST1");
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.getTransaction().commit();
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.close();
checkLazyField(entity, em, testArray);
/**
* Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
* PreUpdate annotated callback method. So state == LazyPropertyInitializer.UNFETCHED_PROPERTY and
* newState == EntityWithLazyProperty.PRE_UPDATE_VALUE. This should no longer cause a ClassCastException.
*/
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
entity.setUpdateLazyFieldInPreUpdate(true);
entity.setSomeField("TEST2");
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.getTransaction().commit();
assertTrue( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.close();
checkLazyField(entity, em, EntityWithLazyProperty.PRE_UPDATE_VALUE);
/**
* Set the updateLazyFieldInPreUpdate flag so that the lazy field is updated from within the
* PreUpdate annotated callback method and also set the lazyData field directly to testArray1. When we reload we
* should get EntityWithLazyProperty.PRE_UPDATE_VALUE.
*/
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
entity.setUpdateLazyFieldInPreUpdate(true);
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
entity.setLazyData(testArray);
assertTrue( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
entity.setSomeField("TEST3");
em.getTransaction().commit();
em.close();
checkLazyField( entity, em, EntityWithLazyProperty.PRE_UPDATE_VALUE);
}
private void checkLazyField(EntityWithLazyProperty entity, EntityManager em, byte[] expected) {
// reload the entity and check the lazy value matches what we expect.
em = getOrCreateEntityManager();
em.getTransaction().begin();
entity = em.find(EntityWithLazyProperty.class, entity.getId());
assertFalse( Hibernate.isPropertyInitialized( entity, "lazyData") );
assertTrue( ArrayHelper.isEquals( expected, entity.getLazyData() ) );
assertTrue( Hibernate.isPropertyInitialized( entity, "lazyData" ) );
em.getTransaction().commit();
em.close();
}
@Override
public Class[] getAnnotatedClasses() {
return new Class[]{
EntityWithLazyProperty.class
};
}
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.callbacks;
package org.hibernate.jpa.test.instrument.domain;
import javax.persistence.*;

View File

@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.instrument.runtime;
import java.lang.reflect.InvocationTargetException;
import org.junit.Rule;
import org.junit.Test;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.buildtime.spi.BasicClassFilter;
import org.hibernate.bytecode.buildtime.spi.FieldFilter;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.InstrumentedClassLoader;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.testing.junit4.ClassLoadingIsolater;
/**
* @author Steve Ebersole
*/
public abstract class AbstractTransformingClassLoaderInstrumentTestCase extends BaseUnitTestCase {
@Rule
public ClassLoadingIsolater isolater = new ClassLoadingIsolater(
new ClassLoadingIsolater.IsolatedClassLoaderProvider() {
final BytecodeProvider provider = buildBytecodeProvider();
@Override
public ClassLoader buildIsolatedClassLoader() {
return new InstrumentedClassLoader(
Thread.currentThread().getContextClassLoader(),
provider.getTransformer(
new BasicClassFilter( new String[] { "org.hibernate.jpa.test.instrument" }, null ),
new FieldFilter() {
public boolean shouldInstrumentField(String className, String fieldName) {
return className.startsWith( "org.hibernate.jpa.test.instrument.domain" );
}
public boolean shouldTransformFieldAccess(String transformingClassName, String fieldOwnerClassName, String fieldName) {
return fieldOwnerClassName.startsWith( "org.hibernate.jpa.test.instrument.domain" )
&& transformingClassName.equals( fieldOwnerClassName );
}
}
)
);
}
@Override
public void releaseIsolatedClassLoader(ClassLoader isolatedClassLoader) {
// nothing to do
}
}
);
protected abstract BytecodeProvider buildBytecodeProvider();
// the tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Test for HHH-7573.
* Load some test data into an entity which has a lazy property and a @PreUpdate callback, then reload and update a
* non lazy field which will trigger the PreUpdate lifecycle callback.
* @throws Exception
*/
@Test
@TestForIssue( jiraKey = "HHH-7573" )
public void LazyPropertyOnPreUpdate() throws Exception {
executeExecutable( "org.hibernate.jpa.test.instrument.cases.TestLazyPropertyOnPreUpdateExecutable" );
}
// reflection code to ensure isolation into the created classloader ~~~~~~~
private static final Class[] SIG = new Class[] {};
private static final Object[] ARGS = new Object[] {};
public void executeExecutable(String name) {
Class execClass = null;
Object executable = null;
try {
execClass = Thread.currentThread().getContextClassLoader().loadClass( name );
executable = execClass.newInstance();
}
catch( Throwable t ) {
throw new HibernateException( "could not load executable", t );
}
try {
execClass.getMethod( "prepare", SIG ).invoke( executable, ARGS );
execClass.getMethod( "execute", SIG ).invoke( executable, ARGS );
}
catch ( NoSuchMethodException e ) {
throw new HibernateException( "could not exeucte executable", e );
}
catch ( IllegalAccessException e ) {
throw new HibernateException( "could not exeucte executable", e );
}
catch ( InvocationTargetException e ) {
throw new HibernateException( "could not exeucte executable", e.getTargetException() );
}
finally {
try {
execClass.getMethod( "complete", SIG ).invoke( executable, ARGS );
}
catch ( Throwable ignore ) {
}
}
}
}

View File

@ -0,0 +1,19 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.jpa.test.instrument.runtime;
import org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl;
import org.hibernate.bytecode.spi.BytecodeProvider;
/**
* @author Steve Ebersole
*/
public class JavassistInstrumentationTest extends AbstractTransformingClassLoaderInstrumentTestCase {
protected BytecodeProvider buildBytecodeProvider() {
return new BytecodeProviderImpl();
}
}