Skip to content

Commit

Permalink
HHH-13551 Ignore ServiceConfigurationError thrown when accessing serv…
Browse files Browse the repository at this point in the history
…ices of individual (non-aggregated) class loaders
  • Loading branch information
yrodiere authored and Sanne committed Aug 13, 2019
1 parent 31fb14e commit 5174fc2
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
Expand Up @@ -15,19 +15,24 @@
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;

import org.hibernate.AssertionFailure;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;

/**
* A service loader bound to an {@link AggregatedClassLoader}.
* @param <S> The type of the service contract.
*/
abstract class AggregatedServiceLoader<S> {

private static final CoreMessageLogger log = CoreLogging.messageLogger( AggregatedServiceLoader.class );

private static final Method SERVICE_LOADER_STREAM_METHOD;
private static final Method PROVIDER_TYPE_METHOD;

Expand Down Expand Up @@ -145,12 +150,14 @@ public void close() {
* @param <S> The type of loaded services.
*/
private static class ClassPathAndModulePathAggregatedServiceLoader<S> extends AggregatedServiceLoader<S> {
private final Class<S> serviceContract;
private final ServiceLoader<S> aggregatedClassLoaderServiceLoader;
private final List<ServiceLoader<S>> delegates;
private Collection<S> cache = null;

private ClassPathAndModulePathAggregatedServiceLoader(AggregatedClassLoader aggregatedClassLoader,
Class<S> serviceContract) {
this.serviceContract = serviceContract;
this.delegates = new ArrayList<>();
this.aggregatedClassLoaderServiceLoader = ServiceLoader.load( serviceContract, aggregatedClassLoader );
final Iterator<ClassLoader> clIterator = aggregatedClassLoader.newClassLoaderIterator();
Expand Down Expand Up @@ -187,16 +194,32 @@ private Collection<S> loadAll() {
Set<S> result = new LinkedHashSet<>();

// Always try the aggregated class loader first
providerStream( aggregatedClassLoaderServiceLoader )
.forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) );
Iterator<? extends Supplier<S>> providerIterator = providerStream( aggregatedClassLoaderServiceLoader )
.iterator();
while ( providerIterator.hasNext() ) {
Supplier<S> provider = providerIterator.next();
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<S> delegate : delegates ) {
providerStream( delegate )
.forEach( provider -> collectServiceIfNotDuplicate( result, alreadyEncountered, provider ) );
providerIterator = providerStream( delegate ).iterator();
/*
* Note that advancing the stream itself can lead to (arguably) "legitimate" errors,
* where we fail to load the service,
* but only because individual classloader has its own definition of the service contract class,
* which is different from ours.
* In that case (still arguably), the error should be ignored.
* That's why we wrap the call to hasNext in a method that catches an logs errors.
* See https://hibernate.atlassian.net/browse/HHH-13551.
*/
while ( hasNextIgnoringServiceConfigurationError( providerIterator ) ) {
Supplier<S> provider = providerIterator.next();
collectServiceIfNotDuplicate( result, alreadyEncountered, provider );
}
}

return result;
Expand All @@ -212,6 +235,17 @@ private Stream<? extends Supplier<S>> providerStream(ServiceLoader<S> serviceLoa
}
}

private boolean hasNextIgnoringServiceConfigurationError(Iterator<?> iterator) {
while ( true ) {
try {
return iterator.hasNext();
}
catch (ServiceConfigurationError e) {
log.ignoringServiceConfigurationError( serviceContract, e );
}
}
}

/*
* We may encounter the same service provider multiple times,
* because the individual class loaders may give access to the same types
Expand Down
Expand Up @@ -15,6 +15,7 @@
import java.sql.SQLWarning;
import java.util.Hashtable;
import java.util.Properties;
import java.util.ServiceConfigurationError;
import java.util.Set;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
Expand Down Expand Up @@ -1871,4 +1872,8 @@ void attemptToAssociateProxyWithTwoOpenSessions(
@Message(value = "Multiple configuration properties defined to create schema. Choose at most one among 'javax.persistence.create-database-schemas', 'hibernate.hbm2ddl.create_namespaces', 'hibernate.hbm2dll.create_namespaces' (this last being deprecated).", id = 504)
void multipleSchemaCreationSettingsDefined();

@LogMessage(level = WARN)
@Message(value = "Ignoring ServiceConfigurationError caught while trying to instantiate service '%s'.", id = 505)
void ignoringServiceConfigurationError(Class<?> serviceContract, @Cause ServiceConfigurationError error);

}

0 comments on commit 5174fc2

Please sign in to comment.