Merge pull request #4602 from eclipse/jetty-10.0.x-4340-ServiceLoader

Issue #4340 - Continue after ServiceLoader ServiceConfigurationError
This commit is contained in:
Lachlan 2020-03-25 10:30:38 +11:00 committed by GitHub
commit af899b0e68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 224 additions and 56 deletions

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.alpn.client;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
@ -32,6 +31,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
import org.eclipse.jetty.io.ssl.ALPNProcessor.Client;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.TypeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -54,19 +54,19 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
IllegalStateException failure = new IllegalStateException("No Client ALPNProcessors!");
// Use a for loop on iterator so load exceptions can be caught and ignored
for (Iterator<Client> i = ServiceLoader.load(Client.class).iterator(); i.hasNext(); )
TypeUtil.serviceProviderStream(ServiceLoader.load(Client.class)).forEach(provider ->
{
Client processor;
try
{
processor = i.next();
processor = provider.get();
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug("Unable to load client processor", x);
failure.addSuppressed(x);
continue;
return;
}
try
@ -80,7 +80,7 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
LOG.debug("Could not initialize " + processor, x);
failure.addSuppressed(x);
}
}
});
if (LOG.isDebugEnabled())
{

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.alpn.server;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import javax.net.ssl.SSLEngine;
@ -30,6 +29,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.ALPNProcessor.Server;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.Name;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -51,12 +51,12 @@ public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFact
IllegalStateException failure = new IllegalStateException("No Server ALPNProcessors!");
// Use a for loop on iterator so load exceptions can be caught and ignored
for (Iterator<Server> i = ServiceLoader.load(Server.class).iterator(); i.hasNext(); )
TypeUtil.serviceProviderStream(ServiceLoader.load(Server.class)).forEach(provider ->
{
Server processor;
try
{
processor = i.next();
processor = provider.get();
}
catch (Throwable x)
{
@ -64,7 +64,7 @@ public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFact
LOG.debug(x.getMessage(), x);
if (x != failure)
failure.addSuppressed(x);
continue;
return;
}
try
@ -79,7 +79,7 @@ public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFact
if (x != failure)
failure.addSuppressed(x);
}
}
});
if (LOG.isDebugEnabled())
{

View File

@ -27,7 +27,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
@ -39,6 +38,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.annotation.HandlesTypes;
@ -815,22 +816,11 @@ public class AnnotationConfiguration extends AbstractConfiguration
long start = 0;
if (LOG.isDebugEnabled())
start = System.nanoTime();
ServiceLoader<ServletContainerInitializer> loader = ServiceLoader.load(ServletContainerInitializer.class);
if (LOG.isDebugEnabled())
LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime() - start), TimeUnit.NANOSECONDS)));
Map<ServletContainerInitializer, Resource> sciResourceMap = new HashMap<ServletContainerInitializer, Resource>();
ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context);
//Get initial set of SCIs that aren't from excluded jars or excluded by the containerExclusionPattern, or excluded
//because containerInitializerOrdering omits it
Iterator<ServletContainerInitializer> iter = loader.iterator();
while (iter.hasNext())
List<ServletContainerInitializer> scis = TypeUtil.serviceProviderStream(ServiceLoader.load(ServletContainerInitializer.class)).flatMap(provider ->
{
ServletContainerInitializer sci;
try
{
sci = iter.next();
return Stream.of(provider.get());
}
catch (Error e)
{
@ -839,9 +829,20 @@ public class AnnotationConfiguration extends AbstractConfiguration
LOG.debug("Error: {} for {}", e.getMessage(), context, e);
else
LOG.info("Error: {} for {}", e.getMessage(), context);
continue;
return Stream.of();
}
}).collect(Collectors.toList());
if (LOG.isDebugEnabled())
LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime() - start), TimeUnit.NANOSECONDS)));
Map<ServletContainerInitializer, Resource> sciResourceMap = new HashMap<>();
ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context);
//Get initial set of SCIs that aren't from excluded jars or excluded by the containerExclusionPattern, or excluded
//because containerInitializerOrdering omits it
for (ServletContainerInitializer sci : scis)
{
if (matchesExclusionPattern(sci))
{
if (LOG.isDebugEnabled())

View File

@ -20,10 +20,10 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
import org.eclipse.jetty.util.TypeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -44,12 +44,11 @@ public class PreEncodedHttpField extends HttpField
static
{
List<HttpFieldPreEncoder> encoders = new ArrayList<>();
Iterator<HttpFieldPreEncoder> iter = ServiceLoader.load(HttpFieldPreEncoder.class).iterator();
while (iter.hasNext())
TypeUtil.serviceProviderStream(ServiceLoader.load(HttpFieldPreEncoder.class)).forEach(provider ->
{
try
{
HttpFieldPreEncoder encoder = iter.next();
HttpFieldPreEncoder encoder = provider.get();
if (index(encoder.getHttpVersion()) >= 0)
encoders.add(encoder);
}
@ -57,7 +56,8 @@ public class PreEncodedHttpField extends HttpField
{
LOG.debug("Unable to add HttpFieldPreEncoder", e);
}
}
});
LOG.debug("HttpField encoders loaded: {}", encoders);
int size = encoders.size();

View File

@ -41,6 +41,7 @@ import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -76,11 +77,8 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
static
{
for (Authenticator.Factory factory : ServiceLoader.load(Authenticator.Factory.class))
{
__knownAuthenticatorFactories.add(factory);
}
TypeUtil.serviceStream(ServiceLoader.load(Authenticator.Factory.class))
.forEach(__knownAuthenticatorFactories::add);
__knownAuthenticatorFactories.add(new DefaultAuthenticatorFactory());
}

View File

@ -0,0 +1,110 @@
//
// ========================================================================
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under
// the terms of the Eclipse Public License 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0
//
// This Source Code may also be made available under the following
// Secondary Licenses when the conditions for such availability set
// forth in the Eclipse Public License, v. 2.0 are satisfied:
// the Apache License v2.0 which is available at
// https://www.apache.org/licenses/LICENSE-2.0
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.util;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Spliterator;
import java.util.function.Consumer;
class ServiceLoaderSpliterator<T> implements Spliterator<ServiceLoader.Provider<T>>
{
private final Iterator<T> iterator;
public ServiceLoaderSpliterator(ServiceLoader<T> serviceLoader)
{
iterator = serviceLoader.iterator();
}
@Override
public boolean tryAdvance(Consumer<? super ServiceLoader.Provider<T>> action)
{
ServiceProvider<T> next;
try
{
if (!iterator.hasNext())
return false;
next = new ServiceProvider<>(iterator.next());
}
catch (Throwable t)
{
next = new ServiceProvider<>(t);
}
action.accept(next);
return true;
}
@Override
public Spliterator<ServiceLoader.Provider<T>> trySplit()
{
return null;
}
@Override
public long estimateSize()
{
return Long.MAX_VALUE;
}
@Override
public int characteristics()
{
return Spliterator.ORDERED;
}
/**
* An implementation of the {@link ServiceLoader.Provider} which contains either an instance of the service or
* an error to be thrown when someone calls {@link #get()}.
* @param <T> the service type.
*/
private static class ServiceProvider<T> implements ServiceLoader.Provider<T>
{
private final T service;
private final Throwable error;
public ServiceProvider(T service)
{
this.service = service;
this.error = null;
}
public ServiceProvider(Throwable error)
{
this.service = null;
this.error = error;
}
@Override
@SuppressWarnings("unchecked")
public Class<? extends T> type()
{
return (Class<? extends T>)get().getClass();
}
@Override
public T get()
{
if (service == null)
throw new ServiceConfigurationError("", error);
return service;
}
}
}

View File

@ -42,6 +42,11 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -751,4 +756,53 @@ public class TypeUtil
}
};
}
/**
* Used on a {@link ServiceLoader#stream()} with {@link Stream#flatMap(Function)},
* so that in the case a {@link ServiceConfigurationError} is thrown it warns and
* continues iterating through the service loader.
* <br>Usage Example:
* <p>{@code ServiceLoader.load(Service.class).stream().flatMap(TypeUtil::providerMap).collect(Collectors.toList());}</p>
* @param <T> The class of the service type.
* @param provider The service provider to instantiate.
* @return a stream of the loaded service providers.
*/
private static <T> Stream<T> mapToService(ServiceLoader.Provider<T> provider)
{
try
{
return Stream.of(provider.get());
}
catch (ServiceConfigurationError error)
{
LOG.warn("Service Provider failed to load", error);
return Stream.empty();
}
}
/**
* Utility method to provide a stream of the service type from a {@link ServiceLoader}.
* Log warnings will be given for any {@link ServiceConfigurationError}s which occur when loading or
* instantiating the services.
* @param serviceLoader the ServiceLoader instance to use.
* @param <T> the type of the service to load.
* @return a stream of the service type which will not throw {@link ServiceConfigurationError}.
*/
public static <T> Stream<T> serviceStream(ServiceLoader<T> serviceLoader)
{
return serviceProviderStream(serviceLoader).flatMap(TypeUtil::mapToService);
}
/**
* Utility to create a stream which provides the same functionality as {@link ServiceLoader#stream()}.
* However this also guards the case in which {@link Iterator#hasNext()} throws. Any exceptions
* from the underlying iterator will be cached until the {@link ServiceLoader.Provider#get()} is called.
* @param serviceLoader the ServiceLoader instance to use.
* @param <T> the type of the service to load.
* @return A stream that lazily loads providers for this loader's service
*/
public static <T> Stream<ServiceLoader.Provider<T>> serviceProviderStream(ServiceLoader<T> serviceLoader)
{
return StreamSupport.stream(new ServiceLoaderSpliterator<>(serviceLoader), false);
}
}

View File

@ -21,7 +21,10 @@ package org.eclipse.jetty.util.security;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.List;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jetty.util.TypeUtil;
import org.slf4j.Logger;
@ -42,7 +45,9 @@ public abstract class Credential implements Serializable
{
private static final long serialVersionUID = -7760551052768181572L;
private static final Logger LOG = LoggerFactory.getLogger(Credential.class);
private static final ServiceLoader<CredentialProvider> CREDENTIAL_PROVIDER_LOADER = ServiceLoader.load(CredentialProvider.class);
private static final List<CredentialProvider> CREDENTIAL_PROVIDERS = TypeUtil.serviceProviderStream(ServiceLoader.load(CredentialProvider.class))
.flatMap(p -> Stream.of(p.get()))
.collect(Collectors.toList());
/**
* Check a credential
@ -68,7 +73,7 @@ public abstract class Credential implements Serializable
if (credential.startsWith(MD5.__TYPE))
return new MD5(credential);
for (CredentialProvider cp : CREDENTIAL_PROVIDER_LOADER)
for (CredentialProvider cp : CREDENTIAL_PROVIDERS)
{
if (credential.startsWith(cp.getPrefix()))
{

View File

@ -37,6 +37,7 @@ import java.util.stream.Collectors;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.TopologicalSort;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
@ -74,27 +75,26 @@ public class Configurations extends AbstractList<Configuration> implements Dumpa
{
if (__known.isEmpty())
{
ServiceLoader<Configuration> configs = ServiceLoader.load(Configuration.class);
for (Iterator<Configuration> i = configs.iterator(); i.hasNext(); )
TypeUtil.serviceProviderStream(ServiceLoader.load(Configuration.class)).forEach(provider ->
{
Configuration configuration = i.next();
try
{
Configuration configuration = provider.get();
if (!configuration.isAvailable())
{
if (LOG.isDebugEnabled())
LOG.debug("Configuration unavailable: " + configuration);
__unavailable.add(configuration);
continue;
return;
}
__known.add(configuration);
__knownByClassName.add(configuration.getClass().getName());
}
catch (Throwable e)
{
LOG.warn("Unable to get known {}", configuration, e);
LOG.warn("Unable to get known Configuration", e);
}
}
});
sort(__known);
if (LOG.isDebugEnabled())

View File

@ -26,23 +26,17 @@ import java.util.Set;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
public class WebSocketExtensionRegistry implements Iterable<Class<? extends Extension>>
{
private Map<String, Class<? extends Extension>> availableExtensions;
private Map<String, Class<? extends Extension>> availableExtensions = new HashMap<>();
public WebSocketExtensionRegistry()
{
// Load extensions from container loader
ServiceLoader<Extension> extensionLoader = ServiceLoader.load(Extension.class, this.getClass().getClassLoader());
availableExtensions = new HashMap<>();
for (Extension ext : extensionLoader)
{
if (ext != null)
{
availableExtensions.put(ext.getName(), ext.getClass());
}
}
// Load extensions from container loader.
TypeUtil.serviceStream(ServiceLoader.load(Extension.class, this.getClass().getClassLoader()))
.forEach(ext -> availableExtensions.put(ext.getName(), ext.getClass()));
}
public Map<String, Class<? extends Extension>> getAvailableExtensions()

View File

@ -22,4 +22,6 @@ module org.eclipse.jetty.websocket.jetty.api
exports org.eclipse.jetty.websocket.api.annotations;
exports org.eclipse.jetty.websocket.api.extensions;
exports org.eclipse.jetty.websocket.api.util;
uses org.eclipse.jetty.websocket.api.extensions.ExtensionConfig.Parser;
}

View File

@ -34,7 +34,7 @@ public interface ExtensionConfig
private static ExtensionConfig.Parser getParser()
{
return ServiceLoader.load(ExtensionConfig.Parser.class).findFirst().get();
return ServiceLoader.load(ExtensionConfig.Parser.class).iterator().next();
}
static ExtensionConfig parse(String parameterizedName)

View File

@ -52,6 +52,8 @@ import java.util.Properties;
import java.util.Queue;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.Loader;
@ -96,7 +98,9 @@ public class XmlConfiguration
{
ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class
};
private static final Iterable<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = ServiceLoader.load(ConfigurationProcessorFactory.class);
private static final List<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = TypeUtil.serviceProviderStream(ServiceLoader.load(ConfigurationProcessorFactory.class))
.flatMap(p -> Stream.of(p.get()))
.collect(Collectors.toList());
private static final XmlParser PARSER = initParser();
public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) ->
{