From f2f788c03d2b5e95c46376d9300297439742b6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 9 Aug 2019 17:42:18 +0200 Subject: [PATCH] HHH-13551 Test the retrieval of a service when an "incompatible" classloader is provided --- .../internal/ClassLoaderServiceImplTest.java | 25 +++++++ .../internal/IsolatedClassLoader.java | 68 +++++++++++++++++++ .../classloading/internal/MyService.java | 12 ++++ .../classloading/internal/MyServiceImpl.java | 10 +++ ...t.registry.classloading.internal.MyService | 1 + 5 files changed, 116 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java create mode 100644 hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java create mode 100644 hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java create mode 100644 hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java index 78a7614108..14dfa58064 100644 --- a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImplTest.java @@ -8,8 +8,11 @@ package org.hibernate.boot.registry.classloading.internal; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -124,6 +127,28 @@ public class ClassLoaderServiceImplTest { csi.stop(); } + @Test + @TestForIssue(jiraKey = "HHH-13551") + public void testServiceFromIncompatibleClassLoader() { + ClassLoaderServiceImpl classLoaderService = new ClassLoaderServiceImpl( + Arrays.asList( + getClass().getClassLoader(), + /* + * This classloader will return instances of MyService where MyService + * is a different object than the one we manipulate in the current classloader. + * This used to throw an exception that triggered a boot failure in ORM, + * but should now be ignored. + */ + new IsolatedClassLoader( getClass().getClassLoader() ) + ), + TcclLookupPrecedence.AFTER + ); + + Collection loadedServices = classLoaderService.loadJavaServices( MyService.class ); + + assertEquals( 1, loadedServices.size() ); + } + private static class InternalClassLoader extends ClassLoader { private List names = new ArrayList<>( ); diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java new file mode 100644 index 0000000000..a1a1a6d5c9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/IsolatedClassLoader.java @@ -0,0 +1,68 @@ +/* + * 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.boot.registry.classloading.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +import org.hibernate.bytecode.spi.ByteCodeHelper; + +/** + * A classloader isolated from the application classloader, + * simulating a separate classloader that can create duplicate classes. + * This can result in a Service implementation extending + * a Service interface that is essentially the same as the one manipulated by ORM, + * but is a different Class instance and is thus deemed different by the JVM. + */ +class IsolatedClassLoader extends ClassLoader { + /** + * Another classloader from which resources will be read. + * Classes available in that classloader will be duplicated in the isolated classloader. + */ + private final ClassLoader resourceSource; + + IsolatedClassLoader(ClassLoader resourceSource) { + super( getTopLevelClassLoader( resourceSource ) ); + this.resourceSource = resourceSource; + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + InputStream is = getResourceAsStream( name.replace( '.', '/' ) + ".class" ); + if ( is == null ) { + throw new ClassNotFoundException( name + " not found" ); + } + + try { + byte[] bytecode = ByteCodeHelper.readByteCode( is ); + return defineClass( name, bytecode, 0, bytecode.length ); + } + catch( Throwable t ) { + throw new ClassNotFoundException( name + " not found", t ); + } + } + + @Override + public URL getResource(String name) { + return resourceSource.getResource( name ); + } + + @Override + public Enumeration getResources(String name) throws IOException { + return resourceSource.getResources( name ); + } + + private static ClassLoader getTopLevelClassLoader(ClassLoader classFileSource) { + ClassLoader result = classFileSource; + while ( result.getParent() != null ) { + result = result.getParent(); + } + return result; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java new file mode 100644 index 0000000000..ce0898803d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyService.java @@ -0,0 +1,12 @@ +/* + * 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.boot.registry.classloading.internal; + +import org.hibernate.service.Service; + +public interface MyService extends Service { +} diff --git a/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java new file mode 100644 index 0000000000..fa9f185022 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/boot/registry/classloading/internal/MyServiceImpl.java @@ -0,0 +1,10 @@ +/* + * 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.boot.registry.classloading.internal; + +public class MyServiceImpl implements MyService { +} diff --git a/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService b/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService new file mode 100644 index 0000000000..89b4c390ee --- /dev/null +++ b/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.classloading.internal.MyService @@ -0,0 +1 @@ +org.hibernate.boot.registry.classloading.internal.MyServiceImpl \ No newline at end of file