/* * 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.test.service; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import javax.persistence.Entity; import org.hibernate.HibernateException; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.integrator.spi.Integrator; import org.hibernate.internal.util.ConfigHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.service.ServiceRegistry; import org.hibernate.testing.TestForIssue; import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; /** * @author Artem V. Navrotskiy * @author Emmanuel Bernard <emmanuel@hibernate.org> */ public class ClassLoaderServiceImplTest { /** * Test for bug: HHH-7084 */ @Test public void testSystemClassLoaderNotOverriding() throws IOException, ClassNotFoundException { Class<?> testClass = Entity.class; // Check that class is accessible by SystemClassLoader. ClassLoader.getSystemClassLoader().loadClass(testClass.getName()); // Create ClassLoader with overridden class. TestClassLoader anotherLoader = new TestClassLoader(); anotherLoader.overrideClass(testClass); Class<?> anotherClass = anotherLoader.loadClass(testClass.getName()); Assert.assertNotSame( testClass, anotherClass ); // Check ClassLoaderServiceImpl().classForName() returns correct class (not from current ClassLoader). ClassLoaderServiceImpl loaderService = new ClassLoaderServiceImpl(anotherLoader); Class<Object> objectClass = loaderService.classForName(testClass.getName()); Assert.assertSame("Should not return class loaded from the parent classloader of ClassLoaderServiceImpl", objectClass, anotherClass); } /** * HHH-8363 discovered multiple leaks within CLS. Most notably, it wasn't getting GC'd due to holding * references to ServiceLoaders. Ensure that the addition of Stoppable functionality cleans up properly. * * TODO: Is there a way to test that the ServiceLoader was actually reset? */ @Test @TestForIssue(jiraKey = "HHH-8363") public void testStoppableClassLoaderService() { final BootstrapServiceRegistryBuilder bootstrapBuilder = new BootstrapServiceRegistryBuilder(); bootstrapBuilder.applyClassLoader( new TestClassLoader() ); final ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder( bootstrapBuilder.build() ).build(); final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); TestIntegrator testIntegrator1 = findTestIntegrator( classLoaderService ); assertNotNull( testIntegrator1 ); TestIntegrator testIntegrator2 = findTestIntegrator( classLoaderService ); assertNotNull( testIntegrator2 ); assertSame( testIntegrator1, testIntegrator2 ); StandardServiceRegistryBuilder.destroy( serviceRegistry ); try { findTestIntegrator( classLoaderService ); Assert.fail("Should have thrown an HibernateException -- the ClassLoaderService instance was closed."); } catch (HibernateException e) { String message = e.getMessage(); Assert.assertEquals( "HHH000469: The ClassLoaderService can not be reused. This instance was stopped already.", message); } } private TestIntegrator findTestIntegrator(ClassLoaderService classLoaderService) { for ( Integrator integrator : classLoaderService.loadJavaServices( Integrator.class ) ) { if ( integrator instanceof TestIntegrator ) { return (TestIntegrator) integrator; } } return null; } private static class TestClassLoader extends ClassLoader { /** * testStoppableClassLoaderService() needs a custom JDK service implementation. Rather than using a real one * on the test classpath, force it in here. */ @Override protected Enumeration<URL> findResources(String name) throws IOException { if (name.equals( "META-INF/services/org.hibernate.integrator.spi.Integrator" )) { final URL serviceUrl = ConfigHelper.findAsResource( "org/hibernate/test/service/org.hibernate.integrator.spi.Integrator" ); return new Enumeration<URL>() { boolean hasMore = true; @Override public boolean hasMoreElements() { return hasMore; } @Override public URL nextElement() { hasMore = false; return serviceUrl; } }; } else { return java.util.Collections.enumeration( java.util.Collections.<URL>emptyList() ); } } /** * Reloading class from binary file. * * @param originalClass Original class. * @throws IOException . */ public void overrideClass(final Class<?> originalClass) throws IOException { String originalPath = "/" + originalClass.getName().replaceAll("\\.", "/") + ".class"; InputStream inputStream = originalClass.getResourceAsStream(originalPath); Assert.assertNotNull(inputStream); try { byte[] data = toByteArray( inputStream ); defineClass(originalClass.getName(), data, 0, data.length); } finally { inputStream.close(); } } private byte[] toByteArray(InputStream inputStream) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); int read; byte[] slice = new byte[2000]; while ( (read = inputStream.read(slice, 0, slice.length) ) != -1) { out.write( slice, 0, read ); } out.flush(); return out.toByteArray(); } } }