HHH-15412 Cleanup: remove reflection keeping JDK8 compatibility afloat in AggregatedServiceLoader
This commit is contained in:
parent
83ea2e8f42
commit
68dc79a87a
|
@ -6,8 +6,6 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.boot.registry.classloading.internal;
|
package org.hibernate.boot.registry.classloading.internal;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -18,10 +16,8 @@ import java.util.List;
|
||||||
import java.util.ServiceConfigurationError;
|
import java.util.ServiceConfigurationError;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.hibernate.AssertionFailure;
|
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
|
||||||
|
@ -33,46 +29,9 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
|
|
||||||
private static final CoreMessageLogger log = CoreLogging.messageLogger( AggregatedServiceLoader.class );
|
private static final CoreMessageLogger log = CoreLogging.messageLogger( AggregatedServiceLoader.class );
|
||||||
|
|
||||||
private static final Method SERVICE_LOADER_STREAM_METHOD;
|
|
||||||
private static final Method PROVIDER_TYPE_METHOD;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Class<?> serviceLoaderClass = ServiceLoader.class;
|
|
||||||
Method serviceLoaderStreamMethod = null;
|
|
||||||
Method providerTypeMethod = null;
|
|
||||||
try {
|
|
||||||
/*
|
|
||||||
* JDK 9 introduced the stream() method on ServiceLoader,
|
|
||||||
* which we need in order to avoid duplicate service instantiation.
|
|
||||||
* See ClassPathAndModulePathAggregatedServiceLoader.
|
|
||||||
*/
|
|
||||||
serviceLoaderStreamMethod = serviceLoaderClass.getMethod( "stream" );
|
|
||||||
Class<?> providerClass = Class.forName( serviceLoaderClass.getName() + "$Provider" );
|
|
||||||
providerTypeMethod = providerClass.getMethod( "type" );
|
|
||||||
}
|
|
||||||
catch (NoSuchMethodException | ClassNotFoundException e) {
|
|
||||||
/*
|
|
||||||
* Probably Java 8.
|
|
||||||
* Leave the method constants null,
|
|
||||||
* we will automatically use a service loader implementation that doesn't rely on them.
|
|
||||||
* See create(...).
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
SERVICE_LOADER_STREAM_METHOD = serviceLoaderStreamMethod;
|
|
||||||
PROVIDER_TYPE_METHOD = providerTypeMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
static <S> AggregatedServiceLoader<S> create(AggregatedClassLoader aggregatedClassLoader,
|
static <S> AggregatedServiceLoader<S> create(AggregatedClassLoader aggregatedClassLoader,
|
||||||
Class<S> serviceContract) {
|
Class<S> serviceContract) {
|
||||||
if ( SERVICE_LOADER_STREAM_METHOD != null ) {
|
|
||||||
// Java 9+
|
|
||||||
return new ClassPathAndModulePathAggregatedServiceLoader<>( aggregatedClassLoader, serviceContract );
|
return new ClassPathAndModulePathAggregatedServiceLoader<>( aggregatedClassLoader, serviceContract );
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Java 8
|
|
||||||
return new ClassPathOnlyAggregatedServiceLoader<>( aggregatedClassLoader, serviceContract );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,44 +44,10 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
*/
|
*/
|
||||||
public abstract void close();
|
public abstract void close();
|
||||||
|
|
||||||
/**
|
|
||||||
* An {@link AggregatedServiceLoader} that will only detect services defined in the class path,
|
|
||||||
* not in the module path,
|
|
||||||
* because it passes the aggregated classloader directly to the service loader.
|
|
||||||
* <p>
|
|
||||||
* This implementation is best when running Hibernate ORM on Java 8.
|
|
||||||
* On Java 9 and above, {@link ClassPathAndModulePathAggregatedServiceLoader} should be used.
|
|
||||||
*
|
|
||||||
* @param <S> The type of loaded services.
|
|
||||||
*/
|
|
||||||
private static class ClassPathOnlyAggregatedServiceLoader<S> extends AggregatedServiceLoader<S> {
|
|
||||||
private final ServiceLoader<S> delegate;
|
|
||||||
|
|
||||||
private ClassPathOnlyAggregatedServiceLoader(AggregatedClassLoader aggregatedClassLoader, Class<S> serviceContract) {
|
|
||||||
this.delegate = ServiceLoader.load( serviceContract, aggregatedClassLoader );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<S> getAll() {
|
|
||||||
final Set<S> services = new LinkedHashSet<>();
|
|
||||||
for ( S service : delegate ) {
|
|
||||||
services.add( service );
|
|
||||||
}
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// Clear service providers
|
|
||||||
delegate.reload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link AggregatedServiceLoader} that will detect services defined in the class path or in the module path.
|
* An {@link AggregatedServiceLoader} that will detect services defined in the class path or in the module path.
|
||||||
* <p>
|
* <p>
|
||||||
* This implementation only works when running Hibernate ORM on Java 9 and above.
|
* This implementation only works when running Hibernate ORM on Java 9 and above.
|
||||||
* On Java 8, {@link ClassPathOnlyAggregatedServiceLoader} must be used.
|
|
||||||
* <p>
|
* <p>
|
||||||
* When retrieving services from providers in the module path,
|
* When retrieving services from providers in the module path,
|
||||||
* the service loader internally uses a map from classloader to service catalog.
|
* the service loader internally uses a map from classloader to service catalog.
|
||||||
|
@ -182,7 +107,7 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
* as well as the close() method,
|
* as well as the close() method,
|
||||||
* and also the service loader map in ClassLoaderServiceImpl,
|
* and also the service loader map in ClassLoaderServiceImpl,
|
||||||
* and we can simply call .reload() on the service loader after we load services
|
* and we can simply call .reload() on the service loader after we load services
|
||||||
* in ClassPathOnlyAggregatedServiceLoader.getAll().
|
* in ClassPathAndModulePathAggregatedServiceLoader.getAll().
|
||||||
*/
|
*/
|
||||||
cache = Collections.unmodifiableCollection( loadAll() );
|
cache = Collections.unmodifiableCollection( loadAll() );
|
||||||
}
|
}
|
||||||
|
@ -194,9 +119,9 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
Set<S> result = new LinkedHashSet<>();
|
Set<S> result = new LinkedHashSet<>();
|
||||||
|
|
||||||
// Always try the aggregated class loader first
|
// Always try the aggregated class loader first
|
||||||
Iterator<? extends Supplier<S>> providerIterator = providerStream( aggregatedClassLoaderServiceLoader ).iterator();
|
Iterator<ServiceLoader.Provider<S>> providerIterator = aggregatedClassLoaderServiceLoader.stream().iterator();
|
||||||
while ( providerIterator.hasNext() ) {
|
while ( providerIterator.hasNext() ) {
|
||||||
Supplier<S> provider = providerIterator.next();
|
ServiceLoader.Provider<S> provider = providerIterator.next();
|
||||||
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );
|
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +130,7 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
* because only them can instantiate services provided by jars in the module path.
|
* because only them can instantiate services provided by jars in the module path.
|
||||||
*/
|
*/
|
||||||
for ( ServiceLoader<S> delegate : delegates ) {
|
for ( ServiceLoader<S> delegate : delegates ) {
|
||||||
providerIterator = providerStream( delegate ).iterator();
|
providerIterator = delegate.stream().iterator();
|
||||||
/*
|
/*
|
||||||
* Note that advancing the stream itself can lead to (arguably) "legitimate" errors,
|
* Note that advancing the stream itself can lead to (arguably) "legitimate" errors,
|
||||||
* where we fail to load the service,
|
* where we fail to load the service,
|
||||||
|
@ -216,7 +141,7 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
* See https://hibernate.atlassian.net/browse/HHH-13551.
|
* See https://hibernate.atlassian.net/browse/HHH-13551.
|
||||||
*/
|
*/
|
||||||
while ( hasNextIgnoringServiceConfigurationError( providerIterator ) ) {
|
while ( hasNextIgnoringServiceConfigurationError( providerIterator ) ) {
|
||||||
Supplier<S> provider = providerIterator.next();
|
ServiceLoader.Provider<S> provider = providerIterator.next();
|
||||||
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );
|
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,16 +149,6 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private Stream<? extends Supplier<S>> providerStream(ServiceLoader<S> serviceLoader) {
|
|
||||||
try {
|
|
||||||
return ( (Stream<? extends Supplier<S>>) SERVICE_LOADER_STREAM_METHOD.invoke( serviceLoader ) );
|
|
||||||
}
|
|
||||||
catch (RuntimeException | IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new AssertionFailure( "Error calling ServiceLoader.stream()", e );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasNextIgnoringServiceConfigurationError(Iterator<?> iterator) {
|
private boolean hasNextIgnoringServiceConfigurationError(Iterator<?> iterator) {
|
||||||
while ( true ) {
|
while ( true ) {
|
||||||
try {
|
try {
|
||||||
|
@ -262,14 +177,8 @@ abstract class AggregatedServiceLoader<S> {
|
||||||
* and one provider may return different types of services
|
* and one provider may return different types of services
|
||||||
* depending on conditions known only to itself.
|
* depending on conditions known only to itself.
|
||||||
*/
|
*/
|
||||||
private void collectServiceIfNotDuplicate(Set<S> result, Set<String> alreadyEncountered, Supplier<S> provider) {
|
private void collectServiceIfNotDuplicate(Set<S> result, Set<String> alreadyEncountered, ServiceLoader.Provider<S> provider) {
|
||||||
Class<?> type;
|
final Class<? extends S> type = provider.type();
|
||||||
try {
|
|
||||||
type = (Class<?>) PROVIDER_TYPE_METHOD.invoke( provider );
|
|
||||||
}
|
|
||||||
catch (RuntimeException | IllegalAccessException | InvocationTargetException e) {
|
|
||||||
throw new AssertionFailure( "Error calling ServiceLoader.Provider.type()", e );
|
|
||||||
}
|
|
||||||
String typeName = type.getName();
|
String typeName = type.getName();
|
||||||
if ( alreadyEncountered.add( typeName ) ) {
|
if ( alreadyEncountered.add( typeName ) ) {
|
||||||
result.add( provider.get() );
|
result.add( provider.get() );
|
||||||
|
|
Loading…
Reference in New Issue