Skip to content

Commit

Permalink
Issue #4340 - ServiceLoader stream which doesn't break if hasNext throws
Browse files Browse the repository at this point in the history
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
  • Loading branch information
lachlan-roberts committed Mar 13, 2020
1 parent 355d213 commit edd6711
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory co
IllegalStateException failure = new IllegalStateException("No Client ALPNProcessors!");

// Use a for loop on iterator so load exceptions can be caught and ignored
ServiceLoader.load(Client.class).stream().flatMap(TypeUtil::providerMap).forEach((processor) ->
TypeUtil.serviceLoaderStream(ServiceLoader.load(Client.class)).flatMap(TypeUtil::providerMap).forEach((processor) ->
{
try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public ALPNServerConnectionFactory(@Name("protocols") String... protocols)

IllegalStateException failure = new IllegalStateException("No Server ALPNProcessors!");
// Use a for loop on iterator so load exceptions can be caught and ignored
ServiceLoader.load(Server.class).stream().flatMap(TypeUtil::providerMap).forEach((processor) ->
TypeUtil.serviceLoaderStream(ServiceLoader.load(Server.class)).flatMap(TypeUtil::providerMap).forEach((processor) ->
{
try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -812,7 +812,7 @@ public List<ServletContainerInitializer> getNonExcludedInitializers(WebAppContex
long start = 0;
if (LOG.isDebugEnabled())
start = System.nanoTime();
List<ServletContainerInitializer> scis = ServiceLoader.load(ServletContainerInitializer.class).stream()
List<ServletContainerInitializer> scis = TypeUtil.serviceLoaderStream(ServiceLoader.load(ServletContainerInitializer.class))
.flatMap(TypeUtil::providerMap)
.collect(Collectors.toList());
if (LOG.isDebugEnabled())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class PreEncodedHttpField extends HttpField

static
{
List<HttpFieldPreEncoder> encoders = ServiceLoader.load(HttpFieldPreEncoder.class).stream()
List<HttpFieldPreEncoder> encoders = TypeUtil.serviceLoaderStream(ServiceLoader.load(HttpFieldPreEncoder.class))
.flatMap(TypeUtil::providerMap)
.filter(encoder -> index(encoder.getHttpVersion()) >= 0)
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti

static
{
ServiceLoader.load(Authenticator.Factory.class).stream()
TypeUtil.serviceLoaderStream(ServiceLoader.load(Authenticator.Factory.class))
.flatMap(TypeUtil::providerMap)
.forEach(__knownAuthenticatorFactories::add);
__knownAuthenticatorFactories.add(new DefaultAuthenticatorFactory());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// ========================================================================
// 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)
{
Provider<T> next = new Provider<>();
try
{
if (!iterator.hasNext())
return false;
next.setServiceProvider(iterator.next());
}
catch (Throwable t)
{
next.setError(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;
}

private static class Provider<T> implements ServiceLoader.Provider<T>
{
private T serviceProvider;
private Throwable error;

public void setServiceProvider(T serviceProvider)
{
this.serviceProvider = serviceProvider;
}

public void setError(Throwable error)
{
this.error = error;
}

@Override
@SuppressWarnings("unchecked")
public Class<? extends T> type()
{
return (Class<? extends T>)get().getClass();
}

@Override
public T get()
{
if (error != null)
throw new ServiceConfigurationError("", error);
return serviceProvider;
}
}
}
14 changes: 14 additions & 0 deletions jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.ServiceLoader;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
Expand Down Expand Up @@ -778,4 +779,17 @@ public static <T> Stream<T> providerMap(ServiceLoader.Provider<T> provider)
return Stream.empty();
}
}

/**
* 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>> serviceLoaderStream(ServiceLoader<T> serviceLoader)
{
return StreamSupport.stream(new ServiceLoaderSpliterator<T>(serviceLoader), false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public abstract class Credential implements Serializable
{
private static final long serialVersionUID = -7760551052768181572L;
private static final Logger LOG = Log.getLogger(Credential.class);
private static final List<CredentialProvider> CREDENTIAL_PROVIDERS = ServiceLoader.load(CredentialProvider.class).stream().flatMap(TypeUtil::providerMap).collect(Collectors.toList());
private static final List<CredentialProvider> CREDENTIAL_PROVIDERS = TypeUtil.serviceLoaderStream(ServiceLoader.load(CredentialProvider.class)).flatMap(TypeUtil::providerMap).collect(Collectors.toList());

/**
* Check a credential
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static synchronized List<Configuration> getKnown()
{
if (__known.isEmpty())
{
ServiceLoader.load(Configuration.class).stream().flatMap(TypeUtil::providerMap).forEach(configuration ->
TypeUtil.serviceLoaderStream(ServiceLoader.load(Configuration.class)).flatMap(TypeUtil::providerMap).forEach(configuration ->
{
if (!configuration.isAvailable())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ public class XmlConfiguration
{
ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class
};
private static final List<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = ServiceLoader.load(ConfigurationProcessorFactory.class)
.stream().flatMap(TypeUtil::providerMap).collect(Collectors.toList());
private static final List<ConfigurationProcessorFactory> PROCESSOR_FACTORIES = TypeUtil.serviceLoaderStream(ServiceLoader.load(ConfigurationProcessorFactory.class))
.flatMap(TypeUtil::providerMap).collect(Collectors.toList());
private static final XmlParser PARSER = initParser();
private static final Comparator<Executable> EXECUTABLE_COMPARATOR = (o1, o2) ->
{
Expand Down

0 comments on commit edd6711

Please sign in to comment.