From bf7f56e0047a599fd13055f2909421682165f4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 15 Oct 2018 15:17:27 +0200 Subject: [PATCH] HHH-13022 Allow to fall back to the OSGi-provided TCCL during bootstrap We previously erased that class loader, which is not a great idea, in particular when we use libraries such as JAXB which rely on the TCCL, to some extent. Without this patch, we cannot make the OSGi integration work correctly with Java 9 and above, because the JAXB APIs won't find the JAXB runtime. --- .../internal/AggregatedClassLoader.java | 231 ++++++++++++++++++ .../internal/ClassLoaderServiceImpl.java | 220 ----------------- hibernate-osgi/hibernate-osgi.gradle | 2 +- .../osgi/OsgiPersistenceProviderService.java | 12 +- .../osgi/OsgiSessionFactoryService.java | 11 +- 5 files changed, 253 insertions(+), 223 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedClassLoader.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedClassLoader.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedClassLoader.java new file mode 100644 index 0000000000..e92ae1b9b4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedClassLoader.java @@ -0,0 +1,231 @@ +/* + * 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.net.URL; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedHashSet; + +public class AggregatedClassLoader extends ClassLoader { + private final ClassLoader[] individualClassLoaders; + private final TcclLookupPrecedence tcclLookupPrecedence; + + public AggregatedClassLoader(final LinkedHashSet orderedClassLoaderSet, TcclLookupPrecedence precedence) { + super( null ); + individualClassLoaders = orderedClassLoaderSet.toArray( new ClassLoader[orderedClassLoaderSet.size()] ); + tcclLookupPrecedence = precedence; + } + + private Iterator newClassLoaderIterator() { + final ClassLoader threadClassLoader = locateTCCL(); + if ( tcclLookupPrecedence == TcclLookupPrecedence.NEVER || threadClassLoader == null ) { + return newTcclNeverIterator(); + } + else if ( tcclLookupPrecedence == TcclLookupPrecedence.AFTER ) { + return newTcclAfterIterator(threadClassLoader); + } + else if ( tcclLookupPrecedence == TcclLookupPrecedence.BEFORE ) { + return newTcclBeforeIterator(threadClassLoader); + } + else { + throw new RuntimeException( "Unknown precedence: "+tcclLookupPrecedence ); + } + } + + private Iterator newTcclBeforeIterator(final ClassLoader threadContextClassLoader) { + final ClassLoader systemClassLoader = locateSystemClassLoader(); + return new Iterator() { + private int currentIndex = 0; + private boolean tcCLReturned = false; + private boolean sysCLReturned = false; + + @Override + public boolean hasNext() { + if ( !tcCLReturned ) { + return true; + } + else if ( currentIndex < individualClassLoaders.length ) { + return true; + } + else if ( !sysCLReturned && systemClassLoader != null ) { + return true; + } + + return false; + } + + @Override + public ClassLoader next() { + if ( !tcCLReturned ) { + tcCLReturned = true; + return threadContextClassLoader; + } + else if ( currentIndex < individualClassLoaders.length ) { + currentIndex += 1; + return individualClassLoaders[ currentIndex - 1 ]; + } + else if ( !sysCLReturned && systemClassLoader != null ) { + sysCLReturned = true; + return systemClassLoader; + } + throw new IllegalStateException( "No more item" ); + } + }; + } + + private Iterator newTcclAfterIterator(final ClassLoader threadContextClassLoader) { + final ClassLoader systemClassLoader = locateSystemClassLoader(); + return new Iterator() { + private int currentIndex = 0; + private boolean tcCLReturned = false; + private boolean sysCLReturned = false; + + @Override + public boolean hasNext() { + if ( currentIndex < individualClassLoaders.length ) { + return true; + } + else if ( !tcCLReturned ) { + return true; + } + else if ( !sysCLReturned && systemClassLoader != null ) { + return true; + } + + return false; + } + + @Override + public ClassLoader next() { + if ( currentIndex < individualClassLoaders.length ) { + currentIndex += 1; + return individualClassLoaders[ currentIndex - 1 ]; + } + else if ( !tcCLReturned ) { + tcCLReturned = true; + return threadContextClassLoader; + } + else if ( !sysCLReturned && systemClassLoader != null ) { + sysCLReturned = true; + return systemClassLoader; + } + throw new IllegalStateException( "No more item" ); + } + }; + } + + private Iterator newTcclNeverIterator() { + final ClassLoader systemClassLoader = locateSystemClassLoader(); + return new Iterator() { + private int currentIndex = 0; + private boolean sysCLReturned = false; + + @Override + public boolean hasNext() { + if ( currentIndex < individualClassLoaders.length ) { + return true; + } + else if ( !sysCLReturned && systemClassLoader != null ) { + return true; + } + + return false; + } + + @Override + public ClassLoader next() { + if ( currentIndex < individualClassLoaders.length ) { + currentIndex += 1; + return individualClassLoaders[ currentIndex - 1 ]; + } + else if ( !sysCLReturned && systemClassLoader != null ) { + sysCLReturned = true; + return systemClassLoader; + } + throw new IllegalStateException( "No more item" ); + } + }; + } + + @Override + public Enumeration getResources(String name) throws IOException { + final LinkedHashSet resourceUrls = new LinkedHashSet(); + final Iterator clIterator = newClassLoaderIterator(); + while ( clIterator.hasNext() ) { + final ClassLoader classLoader = clIterator.next(); + final Enumeration urls = classLoader.getResources( name ); + while ( urls.hasMoreElements() ) { + resourceUrls.add( urls.nextElement() ); + } + } + + return new Enumeration() { + final Iterator resourceUrlIterator = resourceUrls.iterator(); + + @Override + public boolean hasMoreElements() { + return resourceUrlIterator.hasNext(); + } + + @Override + public URL nextElement() { + return resourceUrlIterator.next(); + } + }; + } + + @Override + protected URL findResource(String name) { + final Iterator clIterator = newClassLoaderIterator(); + while ( clIterator.hasNext() ) { + final ClassLoader classLoader = clIterator.next(); + final URL resource = classLoader.getResource( name ); + if ( resource != null ) { + return resource; + } + } + return super.findResource( name ); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + final Iterator clIterator = newClassLoaderIterator(); + while ( clIterator.hasNext() ) { + final ClassLoader classLoader = clIterator.next(); + try { + return classLoader.loadClass( name ); + } + catch (Exception ignore) { + } + catch (LinkageError ignore) { + } + } + + throw new ClassNotFoundException( "Could not load requested class : " + name ); + } + + private static ClassLoader locateSystemClassLoader() { + try { + return ClassLoader.getSystemClassLoader(); + } + catch (Exception e) { + return null; + } + } + + private static ClassLoader locateTCCL() { + try { + return Thread.currentThread().getContextClassLoader(); + } + catch (Exception e) { + return null; + } + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImpl.java index fd4deff3f3..7208caabd4 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/ClassLoaderServiceImpl.java @@ -6,7 +6,6 @@ */ package org.hibernate.boot.registry.classloading.internal; -import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; @@ -17,7 +16,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -126,224 +124,6 @@ public class ClassLoaderServiceImpl implements ClassLoaderService { } } - private static ClassLoader locateSystemClassLoader() { - try { - return ClassLoader.getSystemClassLoader(); - } - catch (Exception e) { - return null; - } - } - - private static ClassLoader locateTCCL() { - try { - return Thread.currentThread().getContextClassLoader(); - } - catch (Exception e) { - return null; - } - } - - private static class AggregatedClassLoader extends ClassLoader { - private final ClassLoader[] individualClassLoaders; - private final TcclLookupPrecedence tcclLookupPrecedence; - - private AggregatedClassLoader(final LinkedHashSet orderedClassLoaderSet, TcclLookupPrecedence precedence) { - super( null ); - individualClassLoaders = orderedClassLoaderSet.toArray( new ClassLoader[orderedClassLoaderSet.size()] ); - tcclLookupPrecedence = precedence; - } - - private Iterator newClassLoaderIterator() { - final ClassLoader threadClassLoader = locateTCCL(); - if ( tcclLookupPrecedence == TcclLookupPrecedence.NEVER || threadClassLoader == null ) { - return newTcclNeverIterator(); - } - else if ( tcclLookupPrecedence == TcclLookupPrecedence.AFTER ) { - return newTcclAfterIterator(threadClassLoader); - } - else if ( tcclLookupPrecedence == TcclLookupPrecedence.BEFORE ) { - return newTcclBeforeIterator(threadClassLoader); - } - else { - throw new RuntimeException( "Unknown precedence: "+tcclLookupPrecedence ); - } - } - - private Iterator newTcclBeforeIterator(final ClassLoader threadContextClassLoader) { - final ClassLoader systemClassLoader = locateSystemClassLoader(); - return new Iterator() { - private int currentIndex = 0; - private boolean tcCLReturned = false; - private boolean sysCLReturned = false; - - @Override - public boolean hasNext() { - if ( !tcCLReturned ) { - return true; - } - else if ( currentIndex < individualClassLoaders.length ) { - return true; - } - else if ( !sysCLReturned && systemClassLoader != null ) { - return true; - } - - return false; - } - - @Override - public ClassLoader next() { - if ( !tcCLReturned ) { - tcCLReturned = true; - return threadContextClassLoader; - } - else if ( currentIndex < individualClassLoaders.length ) { - currentIndex += 1; - return individualClassLoaders[ currentIndex - 1 ]; - } - else if ( !sysCLReturned && systemClassLoader != null ) { - sysCLReturned = true; - return systemClassLoader; - } - throw new IllegalStateException( "No more item" ); - } - }; - } - - private Iterator newTcclAfterIterator(final ClassLoader threadContextClassLoader) { - final ClassLoader systemClassLoader = locateSystemClassLoader(); - return new Iterator() { - private int currentIndex = 0; - private boolean tcCLReturned = false; - private boolean sysCLReturned = false; - - @Override - public boolean hasNext() { - if ( currentIndex < individualClassLoaders.length ) { - return true; - } - else if ( !tcCLReturned ) { - return true; - } - else if ( !sysCLReturned && systemClassLoader != null ) { - return true; - } - - return false; - } - - @Override - public ClassLoader next() { - if ( currentIndex < individualClassLoaders.length ) { - currentIndex += 1; - return individualClassLoaders[ currentIndex - 1 ]; - } - else if ( !tcCLReturned ) { - tcCLReturned = true; - return threadContextClassLoader; - } - else if ( !sysCLReturned && systemClassLoader != null ) { - sysCLReturned = true; - return systemClassLoader; - } - throw new IllegalStateException( "No more item" ); - } - }; - } - - private Iterator newTcclNeverIterator() { - final ClassLoader systemClassLoader = locateSystemClassLoader(); - return new Iterator() { - private int currentIndex = 0; - private boolean sysCLReturned = false; - - @Override - public boolean hasNext() { - if ( currentIndex < individualClassLoaders.length ) { - return true; - } - else if ( !sysCLReturned && systemClassLoader != null ) { - return true; - } - - return false; - } - - @Override - public ClassLoader next() { - if ( currentIndex < individualClassLoaders.length ) { - currentIndex += 1; - return individualClassLoaders[ currentIndex - 1 ]; - } - else if ( !sysCLReturned && systemClassLoader != null ) { - sysCLReturned = true; - return systemClassLoader; - } - throw new IllegalStateException( "No more item" ); - } - }; - } - - @Override - public Enumeration getResources(String name) throws IOException { - final LinkedHashSet resourceUrls = new LinkedHashSet(); - final Iterator clIterator = newClassLoaderIterator(); - while ( clIterator.hasNext() ) { - final ClassLoader classLoader = clIterator.next(); - final Enumeration urls = classLoader.getResources( name ); - while ( urls.hasMoreElements() ) { - resourceUrls.add( urls.nextElement() ); - } - } - - return new Enumeration() { - final Iterator resourceUrlIterator = resourceUrls.iterator(); - - @Override - public boolean hasMoreElements() { - return resourceUrlIterator.hasNext(); - } - - @Override - public URL nextElement() { - return resourceUrlIterator.next(); - } - }; - } - - @Override - protected URL findResource(String name) { - final Iterator clIterator = newClassLoaderIterator(); - while ( clIterator.hasNext() ) { - final ClassLoader classLoader = clIterator.next(); - final URL resource = classLoader.getResource( name ); - if ( resource != null ) { - return resource; - } - } - return super.findResource( name ); - } - - @Override - protected Class findClass(String name) throws ClassNotFoundException { - final Iterator clIterator = newClassLoaderIterator(); - while ( clIterator.hasNext() ) { - final ClassLoader classLoader = clIterator.next(); - try { - return classLoader.loadClass( name ); - } - catch (Exception ignore) { - } - catch (LinkageError ignore) { - } - } - - throw new ClassNotFoundException( "Could not load requested class : " + name ); - } - - } - @Override @SuppressWarnings({"unchecked"}) public Class classForName(String className) { diff --git a/hibernate-osgi/hibernate-osgi.gradle b/hibernate-osgi/hibernate-osgi.gradle index e83593b8cc..b667330cb1 100644 --- a/hibernate-osgi/hibernate-osgi.gradle +++ b/hibernate-osgi/hibernate-osgi.gradle @@ -49,7 +49,7 @@ dependencies { // this dependency wasn't there in the 5.2.x bundles so ignoring it for now // we might reintroduce it at some point if users complain about it exclude module: 'javax.activation-api' - // TODO HHH-13022 Find a way to include JAXB in the OSGi feature + // JAXB is included in the Karaf distribution exclude module: 'jaxb-api' exclude module: 'jaxb-runtime' } diff --git a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiPersistenceProviderService.java b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiPersistenceProviderService.java index ac3857d6c0..4c8cc6ef5f 100644 --- a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiPersistenceProviderService.java +++ b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiPersistenceProviderService.java @@ -6,7 +6,11 @@ */ package org.hibernate.osgi; +import java.util.LinkedHashSet; + import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader; +import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence; import org.hibernate.jpa.HibernateEntityManagerFactory; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; @@ -52,7 +56,13 @@ public class OsgiPersistenceProviderService implements ServiceFactory { // ClassLoaderService now. final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader( osgiClassLoader ); + LinkedHashSet newTcclDelegates = new LinkedHashSet<>(); + newTcclDelegates.add( osgiClassLoader ); + newTcclDelegates.add( originalTccl ); + final ClassLoader newTccl = new AggregatedClassLoader( + newTcclDelegates, TcclLookupPrecedence.NEVER + ); + Thread.currentThread().setContextClassLoader( newTccl ); try { return new OsgiPersistenceProvider( osgiClassLoader, osgiJtaPlatform, osgiServiceUtil, requestingBundle ); } diff --git a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiSessionFactoryService.java b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiSessionFactoryService.java index cba56af3ef..618aeb077d 100644 --- a/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiSessionFactoryService.java +++ b/hibernate-osgi/src/main/java/org/hibernate/osgi/OsgiSessionFactoryService.java @@ -14,6 +14,8 @@ import org.hibernate.boot.registry.BootstrapServiceRegistry; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.registry.classloading.internal.AggregatedClassLoader; +import org.hibernate.boot.registry.classloading.internal.TcclLookupPrecedence; import org.hibernate.boot.registry.selector.StrategyRegistrationProvider; import org.hibernate.cfg.AvailableSettings; import org.hibernate.integrator.spi.Integrator; @@ -26,6 +28,7 @@ import org.osgi.framework.ServiceRegistration; import org.osgi.framework.wiring.BundleWiring; import java.util.Collection; +import java.util.LinkedHashSet; /** * Hibernate 4.2 and 4.3 still heavily rely on TCCL for ClassLoading. Although @@ -78,7 +81,13 @@ public class OsgiSessionFactoryService implements ServiceFactory { // ClassLoaderService now. final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader( osgiClassLoader ); + LinkedHashSet newTcclDelegates = new LinkedHashSet<>(); + newTcclDelegates.add( osgiClassLoader ); + newTcclDelegates.add( originalTccl ); + final ClassLoader newTccl = new AggregatedClassLoader( + newTcclDelegates, TcclLookupPrecedence.NEVER + ); + Thread.currentThread().setContextClassLoader( newTccl ); try { return buildSessionFactory( requestingBundle, osgiClassLoader ); }