HHH-16911 Integration test for empty SessionFactory to not leak the CL

This commit is contained in:
Sanne Grinovero 2023-07-17 19:16:43 +01:00 committed by Sanne Grinovero
parent 187e637b68
commit 6ff15ff3fb
2 changed files with 129 additions and 0 deletions

View File

@ -0,0 +1,71 @@
/*
* 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.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Set;
import java.util.stream.Collectors;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.junit.Assert;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* Verifies that Hibernate ORM won't leak the classloader.
* We only test in H2 to save some time; we also need to avoid
* leaks by JDBC drivers, and since these aren't our responsibility
* it's best to focus on a single DB.
*/
@RequiresDialect(H2Dialect.class)
public class HibernateClassLoaderLeaksTest {
private static Set<Driver> knownDrivers;
@BeforeAll
public static void prepareForClassLoaderLeakTest() {
final String property = System.getProperty( "log4j2.disableJmx" );
Assert.assertEquals( "To be able to test against leaks, the system property 'log4j2.disableJmx' must be set to true",
"true", property );
//Attempt to workaround the mess of DriverManager leaks by clearing it before the test;
//it will most certainly re-register all drivers again within the test running context,
//but that will imply that the isolated classloader will also have permission to de-register them.
knownDrivers = DriverManager.drivers().collect( Collectors.toUnmodifiableSet() );
knownDrivers.forEach( HibernateClassLoaderLeaksTest::cleanup );
}
@AfterAll
public static void restoreRegisteredDrivers() throws SQLException {
if ( knownDrivers != null ) {
for ( Driver driver : knownDrivers ) {
DriverManager.registerDriver( driver );
}
}
}
@Test
public void hibernateDoesNotLeakClassloader() {
ClassLoaderLeakDetector.assertNotLeakingAction( HibernateLoadingTestAction.class.getName() );
}
private static void cleanup(Driver driver) {
System.out.println( "Attempting de-registration of driver: " + driver );
try {
DriverManager.deregisterDriver( driver );
}
catch ( SQLException e ) {
throw new RuntimeException( e );
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import jakarta.persistence.EntityManagerFactory;
/**
* A Runnable which initializes an EntityManagerFactory;
* this is meant to test against classloader leaks, so needs
* to be packaged as a Runnable rather than using our usual
* testing facilities.
*/
public class HibernateLoadingTestAction extends NotLeakingTestAction implements Runnable {
@Override
public void run() {
super.run(); //for basic sanity self-check
final Map config = new HashMap();
EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder(
new BaseEntityManagerFunctionalTestCase.TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() ),
config
).build();
try {
emf.close();
}
finally {
cleanupJDBCDrivers();
}
}
private void cleanupJDBCDrivers() {
DriverManager.drivers().forEach( this::deregister );
}
private void deregister(final Driver driver) {
System.out.println( "Unregistering driver: " +driver);
try {
DriverManager.deregisterDriver( driver );
}
catch ( SQLException e ) {
throw new RuntimeException( e );
}
}
}