HHH-16911 Integration test for SessionFactory using callbacks to not leak ClassLoader

This commit is contained in:
Sanne Grinovero 2023-07-18 18:50:14 +01:00 committed by Sanne Grinovero
parent 6ff15ff3fb
commit f3e11f1610
4 changed files with 95 additions and 5 deletions

View File

@ -0,0 +1,53 @@
/*
* 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.orm.test.bootstrap.registry.classloading;
import java.util.Collections;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Id;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Transient;
/**
* A test scenario to be used with {@link HibernateClassLoaderLeaksTest};
* the crucial aspect is that we're triggering a lookup of a JPA callback
* method.
*/
public class HibernateCallbacksTestAction extends HibernateLoadingTestAction {
protected void actionOnHibernate(EntityManagerFactory emf) {
try (final EntityManager entityManager = emf.createEntityManager() ) {
Booking b = new Booking();
b.id = Long.valueOf( 1l );
entityManager.persist( b ); //to trigger the @PrePersist invocation
}
}
protected List<String> getManagedClassNames() {
return Collections.singletonList( Booking.class.getName() );
}
@Entity(name = "booking")
private static class Booking {
@Id Long id;
@Transient String legacyIdentifier;
@PrePersist
public void computeLegacyIdentifier() {
//Details are not important, just making something up.
if ( legacyIdentifier == null && id != null ) {
this.legacyIdentifier = id.toString();
}
}
}
}

View File

@ -58,6 +58,11 @@ public class HibernateClassLoaderLeaksTest {
ClassLoaderLeakDetector.assertNotLeakingAction( HibernateLoadingTestAction.class.getName() ); ClassLoaderLeakDetector.assertNotLeakingAction( HibernateLoadingTestAction.class.getName() );
} }
@Test
public void hibernateDoesNotLeakClassloaderWithCallbacks() {
ClassLoaderLeakDetector.assertNotLeakingAction( HibernateCallbacksTestAction.class.getName() );
}
private static void cleanup(Driver driver) { private static void cleanup(Driver driver) {
System.out.println( "Attempting de-registration of driver: " + driver ); System.out.println( "Attempting de-registration of driver: " + driver );
try { try {

View File

@ -9,9 +9,12 @@ package org.hibernate.orm.test.bootstrap.registry.classloading;
import java.sql.Driver; import java.sql.Driver;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.hibernate.SessionFactory;
import org.hibernate.jpa.boot.spi.Bootstrap; import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
@ -22,25 +25,50 @@ import jakarta.persistence.EntityManagerFactory;
* this is meant to test against classloader leaks, so needs * this is meant to test against classloader leaks, so needs
* to be packaged as a Runnable rather than using our usual * to be packaged as a Runnable rather than using our usual
* testing facilities. * testing facilities.
* @see HibernateClassLoaderLeaksTest
*/ */
public class HibernateLoadingTestAction extends NotLeakingTestAction implements Runnable { public class HibernateLoadingTestAction extends NotLeakingTestAction implements Runnable {
@Override @Override
public void run() { public final void run() {
super.run(); //for basic sanity self-check super.run(); //for basic sanity self-check
final Map config = new HashMap(); final Map config = new HashMap();
EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder( EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder(
new BaseEntityManagerFunctionalTestCase.TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ), new BaseEntityManagerFunctionalTestCase.TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ) {
@Override
public boolean isExcludeUnlistedClasses() {
return true;
}
@Override
public List<String> getManagedClassNames() {
return HibernateLoadingTestAction.this.getManagedClassNames();
}
},
config config
).build(); ).build();
try { try {
emf.close(); checkExpectedClassLoader( emf.unwrap( SessionFactory.class ).getClass() );
actionOnHibernate( emf );
} }
finally { finally {
cleanupJDBCDrivers(); try {
emf.close();
}
finally {
cleanupJDBCDrivers();
}
} }
} }
protected void actionOnHibernate(EntityManagerFactory emf) {
//no-op
}
protected List<String> getManagedClassNames() {
return Collections.emptyList();
}
private void cleanupJDBCDrivers() { private void cleanupJDBCDrivers() {
DriverManager.drivers().forEach( this::deregister ); DriverManager.drivers().forEach( this::deregister );
} }

View File

@ -14,7 +14,11 @@ public class NotLeakingTestAction implements Runnable {
@Override @Override
public void run() { public void run() {
final ClassLoader owningClassloader = getClass().getClassLoader(); checkExpectedClassLoader( getClass() );
}
protected void checkExpectedClassLoader(Class aClass) {
final ClassLoader owningClassloader = aClass.getClassLoader();
if ( !owningClassloader.getName().equals( "TestIsolatedIsolatedClassLoader" ) ) { if ( !owningClassloader.getName().equals( "TestIsolatedIsolatedClassLoader" ) ) {
throw new IllegalStateException( "Not being loaded by the expected classloader" ); throw new IllegalStateException( "Not being loaded by the expected classloader" );
} }