Skip to content

Commit

Permalink
HHH-16911 Integration test for empty SessionFactory to not leak the CL
Browse files Browse the repository at this point in the history
  • Loading branch information
Sanne committed Aug 1, 2023
1 parent 187e637 commit 6ff15ff
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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 );
}
}

}
Original file line number Diff line number Diff line change
@@ -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 );
}
}

}

0 comments on commit 6ff15ff

Please sign in to comment.