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.
This commit is contained in:
Yoann Rodière 2018-10-15 15:17:27 +02:00 committed by Guillaume Smet
parent 6ef94f3ba7
commit bf7f56e004
5 changed files with 253 additions and 223 deletions

View File

@ -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<ClassLoader> orderedClassLoaderSet, TcclLookupPrecedence precedence) {
super( null );
individualClassLoaders = orderedClassLoaderSet.toArray( new ClassLoader[orderedClassLoaderSet.size()] );
tcclLookupPrecedence = precedence;
}
private Iterator<ClassLoader> 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<ClassLoader> newTcclBeforeIterator(final ClassLoader threadContextClassLoader) {
final ClassLoader systemClassLoader = locateSystemClassLoader();
return new Iterator<ClassLoader>() {
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<ClassLoader> newTcclAfterIterator(final ClassLoader threadContextClassLoader) {
final ClassLoader systemClassLoader = locateSystemClassLoader();
return new Iterator<ClassLoader>() {
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<ClassLoader> newTcclNeverIterator() {
final ClassLoader systemClassLoader = locateSystemClassLoader();
return new Iterator<ClassLoader>() {
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<URL> getResources(String name) throws IOException {
final LinkedHashSet<URL> resourceUrls = new LinkedHashSet<URL>();
final Iterator<ClassLoader> clIterator = newClassLoaderIterator();
while ( clIterator.hasNext() ) {
final ClassLoader classLoader = clIterator.next();
final Enumeration<URL> urls = classLoader.getResources( name );
while ( urls.hasMoreElements() ) {
resourceUrls.add( urls.nextElement() );
}
}
return new Enumeration<URL>() {
final Iterator<URL> 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<ClassLoader> 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<ClassLoader> 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;
}
}
}

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.boot.registry.classloading.internal; package org.hibernate.boot.registry.classloading.internal;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
@ -17,7 +16,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; 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<ClassLoader> orderedClassLoaderSet, TcclLookupPrecedence precedence) {
super( null );
individualClassLoaders = orderedClassLoaderSet.toArray( new ClassLoader[orderedClassLoaderSet.size()] );
tcclLookupPrecedence = precedence;
}
private Iterator<ClassLoader> 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<ClassLoader> newTcclBeforeIterator(final ClassLoader threadContextClassLoader) {
final ClassLoader systemClassLoader = locateSystemClassLoader();
return new Iterator<ClassLoader>() {
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<ClassLoader> newTcclAfterIterator(final ClassLoader threadContextClassLoader) {
final ClassLoader systemClassLoader = locateSystemClassLoader();
return new Iterator<ClassLoader>() {
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<ClassLoader> newTcclNeverIterator() {
final ClassLoader systemClassLoader = locateSystemClassLoader();
return new Iterator<ClassLoader>() {
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<URL> getResources(String name) throws IOException {
final LinkedHashSet<URL> resourceUrls = new LinkedHashSet<URL>();
final Iterator<ClassLoader> clIterator = newClassLoaderIterator();
while ( clIterator.hasNext() ) {
final ClassLoader classLoader = clIterator.next();
final Enumeration<URL> urls = classLoader.getResources( name );
while ( urls.hasMoreElements() ) {
resourceUrls.add( urls.nextElement() );
}
}
return new Enumeration<URL>() {
final Iterator<URL> 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<ClassLoader> 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<ClassLoader> 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 @Override
@SuppressWarnings({"unchecked"}) @SuppressWarnings({"unchecked"})
public <T> Class<T> classForName(String className) { public <T> Class<T> classForName(String className) {

View File

@ -49,7 +49,7 @@ dependencies {
// this dependency wasn't there in the 5.2.x bundles so ignoring it for now // 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 // we might reintroduce it at some point if users complain about it
exclude module: 'javax.activation-api' 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-api'
exclude module: 'jaxb-runtime' exclude module: 'jaxb-runtime'
} }

View File

@ -6,7 +6,11 @@
*/ */
package org.hibernate.osgi; package org.hibernate.osgi;
import java.util.LinkedHashSet;
import org.hibernate.SessionFactory; 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.hibernate.jpa.HibernateEntityManagerFactory;
import org.osgi.framework.Bundle; import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil; import org.osgi.framework.FrameworkUtil;
@ -52,7 +56,13 @@ public class OsgiPersistenceProviderService implements ServiceFactory {
// ClassLoaderService now. // ClassLoaderService now.
final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader( osgiClassLoader ); LinkedHashSet<ClassLoader> newTcclDelegates = new LinkedHashSet<>();
newTcclDelegates.add( osgiClassLoader );
newTcclDelegates.add( originalTccl );
final ClassLoader newTccl = new AggregatedClassLoader(
newTcclDelegates, TcclLookupPrecedence.NEVER
);
Thread.currentThread().setContextClassLoader( newTccl );
try { try {
return new OsgiPersistenceProvider( osgiClassLoader, osgiJtaPlatform, osgiServiceUtil, requestingBundle ); return new OsgiPersistenceProvider( osgiClassLoader, osgiJtaPlatform, osgiServiceUtil, requestingBundle );
} }

View File

@ -14,6 +14,8 @@ import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; 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.boot.registry.selector.StrategyRegistrationProvider;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.integrator.spi.Integrator; import org.hibernate.integrator.spi.Integrator;
@ -26,6 +28,7 @@ import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.wiring.BundleWiring; import org.osgi.framework.wiring.BundleWiring;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashSet;
/** /**
* Hibernate 4.2 and 4.3 still heavily rely on TCCL for ClassLoading. Although * 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. // ClassLoaderService now.
final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader(); final ClassLoader originalTccl = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader( osgiClassLoader ); LinkedHashSet<ClassLoader> newTcclDelegates = new LinkedHashSet<>();
newTcclDelegates.add( osgiClassLoader );
newTcclDelegates.add( originalTccl );
final ClassLoader newTccl = new AggregatedClassLoader(
newTcclDelegates, TcclLookupPrecedence.NEVER
);
Thread.currentThread().setContextClassLoader( newTccl );
try { try {
return buildSessionFactory( requestingBundle, osgiClassLoader ); return buildSessionFactory( requestingBundle, osgiClassLoader );
} }