From 31fb14e0d9de1df1aa239e85542df54d992248bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 9 Aug 2019 19:02:03 +0200 Subject: [PATCH] HHH-13551 Restucture ClassPathAndModulePathAggregatedServiceLoader This does not change the behavior of the class at all: it simply restructures the code to allow for the changes in the next commits. --- .../internal/AggregatedServiceLoader.java | 104 ++++++++++-------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java index eead6d561a..edc8907c56 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/classloading/internal/AggregatedServiceLoader.java @@ -145,17 +145,14 @@ abstract class AggregatedServiceLoader { * @param The type of loaded services. */ private static class ClassPathAndModulePathAggregatedServiceLoader extends AggregatedServiceLoader { + private final ServiceLoader aggregatedClassLoaderServiceLoader; private final List> delegates; private Collection cache = null; private ClassPathAndModulePathAggregatedServiceLoader(AggregatedClassLoader aggregatedClassLoader, Class serviceContract) { this.delegates = new ArrayList<>(); - // Always try the aggregated class loader first - this.delegates.add( ServiceLoader.load( serviceContract, aggregatedClassLoader ) ); - - // Then also try the individual class loaders, - // because only them can instantiate services provided by jars in the module path + this.aggregatedClassLoaderServiceLoader = ServiceLoader.load( serviceContract, aggregatedClassLoader ); final Iterator clIterator = aggregatedClassLoader.newClassLoaderIterator(); while ( clIterator.hasNext() ) { this.delegates.add( @@ -185,54 +182,67 @@ abstract class AggregatedServiceLoader { return cache; } - @SuppressWarnings("unchecked") private Collection loadAll() { Set alreadyEncountered = new HashSet<>(); Set result = new LinkedHashSet<>(); - delegates.stream() - // Each loader's stream() method returns a stream of service providers: flatten these into a single stream - .flatMap( delegate -> { - try { - return (Stream>) SERVICE_LOADER_STREAM_METHOD.invoke( delegate ); - } - catch (RuntimeException | IllegalAccessException | InvocationTargetException e) { - throw new AssertionFailure( "Error calling ServiceLoader.stream()", e ); - } - } ) - // For each provider, check its type to be sure we don't use a provider twice, then get the service - .forEach( provider -> { - Class 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(); - /* - * We may encounter the same service provider multiple times, - * because the individual class loaders may give access to the same types - * (at the very least a single class loader may be present twice in the aggregated class loader). - * However, we only want to get the service from each provider once. - * - * ServiceLoader.stream() is useful in that regard, - * since it allows us to check the type of the service provider - * before the service is even instantiated. - * - * We could just instantiate every service and check their type afterwards, - * but 1. it would lead to unnecessary instantiation which could have side effects, - * in particular regarding class loading, - * and 2. the type of the provider may not always be the type of the service, - * and one provider may return different types of services - * depending on conditions known only to itself. - */ - if ( alreadyEncountered.add( typeName ) ) { - result.add( provider.get() ); - } - } ); + + // Always try the aggregated class loader first + providerStream( aggregatedClassLoaderServiceLoader ) + .forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) ); + + /* + * Then also try the individual class loaders, + * because only them can instantiate services provided by jars in the module path. + */ + for ( ServiceLoader delegate : delegates ) { + providerStream( delegate ) + .forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) ); + } + return result; } + @SuppressWarnings("unchecked") + private Stream> providerStream(ServiceLoader serviceLoader) { + try { + return ( (Stream>) SERVICE_LOADER_STREAM_METHOD.invoke( serviceLoader ) ); + } + catch (RuntimeException | IllegalAccessException | InvocationTargetException e) { + throw new AssertionFailure( "Error calling ServiceLoader.stream()", e ); + } + } + + /* + * We may encounter the same service provider multiple times, + * because the individual class loaders may give access to the same types + * (at the very least a single class loader may be present twice in the aggregated class loader). + * However, we only want to get the service from each provider once. + * + * ServiceLoader.stream() is useful in that regard, + * since it allows us to check the type of the service provider + * before the service is even instantiated. + * + * We could just instantiate every service and check their type afterwards, + * but 1. it would lead to unnecessary instantiation which could have side effects, + * in particular regarding class loading, + * and 2. the type of the provider may not always be the type of the service, + * and one provider may return different types of services + * depending on conditions known only to itself. + */ + private void collectServiceIfNotDuplicate(Set result, Set alreadyEncountered, Supplier provider) { + Class 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(); + if ( alreadyEncountered.add( typeName ) ) { + result.add( provider.get() ); + } + } + @Override public void close() { cache = null;