|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package java.util; |
|
|
|
import java.io.BufferedReader; |
|
import java.io.IOException; |
|
import java.io.InputStream; |
|
import java.io.InputStreamReader; |
|
import java.lang.reflect.Constructor; |
|
import java.lang.reflect.InvocationTargetException; |
|
import java.lang.reflect.Method; |
|
import java.lang.reflect.Modifier; |
|
import java.net.URL; |
|
import java.net.URLConnection; |
|
import java.security.AccessControlContext; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.security.PrivilegedActionException; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.util.function.Consumer; |
|
import java.util.function.Supplier; |
|
import java.util.stream.Stream; |
|
import java.util.stream.StreamSupport; |
|
|
|
import jdk.internal.loader.BootLoader; |
|
import jdk.internal.loader.ClassLoaders; |
|
import jdk.internal.misc.JavaLangAccess; |
|
import jdk.internal.misc.SharedSecrets; |
|
import jdk.internal.misc.VM; |
|
import jdk.internal.module.ServicesCatalog; |
|
import jdk.internal.module.ServicesCatalog.ServiceProvider; |
|
import jdk.internal.reflect.CallerSensitive; |
|
import jdk.internal.reflect.Reflection; |
|
|
|
|
|
/** |
|
* A facility to load implementations of a service. |
|
* |
|
* <p> A <i>service</i> is a well-known interface or class for which zero, one, |
|
* or many service providers exist. A <i>service provider</i> (or just |
|
* <i>provider</i>) is a class that implements or subclasses the well-known |
|
* interface or class. A {@code ServiceLoader} is an object that locates and |
|
* loads service providers deployed in the run time environment at a time of an |
|
* application's choosing. Application code refers only to the service, not to |
|
* service providers, and is assumed to be capable of differentiating between |
|
* multiple service providers as well as handling the possibility that no service |
|
* providers are located. |
|
* |
|
* <h3> Obtaining a service loader </h3> |
|
* |
|
* <p> An application obtains a service loader for a given service by invoking |
|
* one of the static {@code load} methods of ServiceLoader. If the application |
|
* is a module, then its module declaration must have a <i>uses</i> directive |
|
* that specifies the service; this helps to locate providers and ensure they |
|
* will execute reliably. In addition, if the service is not in the application |
|
* module, then the module declaration must have a <i>requires</i> directive |
|
* that specifies the module which exports the service. |
|
* |
|
* <p> A service loader can be used to locate and instantiate providers of the |
|
* service by means of the {@link #iterator() iterator} method. {@code ServiceLoader} |
|
* also defines the {@link #stream() stream} method to obtain a stream of providers |
|
* that can be inspected and filtered without instantiating them. |
|
* |
|
* <p> As an example, suppose the service is {@code com.example.CodecFactory}, an |
|
* interface that defines methods for producing encoders and decoders: |
|
* |
|
* <pre>{@code |
|
* package com.example; |
|
* public interface CodecFactory { |
|
* Encoder getEncoder(String encodingName); |
|
* Decoder getDecoder(String encodingName); |
|
* } |
|
* }</pre> |
|
* |
|
* <p> The following code obtains a service loader for the {@code CodecFactory} |
|
* service, then uses its iterator (created automatically by the enhanced-for |
|
* loop) to yield instances of the service providers that are located: |
|
* |
|
* <pre>{@code |
|
* ServiceLoader<CodecFactory> loader = ServiceLoader.load(CodecFactory.class); |
|
* for (CodecFactory factory : loader) { |
|
* Encoder enc = factory.getEncoder("PNG"); |
|
* if (enc != null) |
|
* ... use enc to encode a PNG file |
|
* break; |
|
* } |
|
* }</pre> |
|
* |
|
* <p> If this code resides in a module, then in order to refer to the |
|
* {@code com.example.CodecFactory} interface, the module declaration would |
|
* require the module which exports the interface. The module declaration would |
|
* also specify use of {@code com.example.CodecFactory}: |
|
* <pre>{@code |
|
* requires com.example.codec.core; |
|
* uses com.example.CodecFactory; |
|
* }</pre> |
|
* |
|
* <p> Sometimes an application may wish to inspect a service provider before |
|
* instantiating it, in order to determine if an instance of that service |
|
* provider would be useful. For example, a service provider for {@code |
|
* CodecFactory} that is capable of producing a "PNG" encoder may be annotated |
|
* with {@code @PNG}. The following code uses service loader's {@code stream} |
|
* method to yield instances of {@code Provider<CodecFactory>} in contrast to |
|
* how the iterator yields instances of {@code CodecFactory}: |
|
* <pre>{@code |
|
* ServiceLoader<CodecFactory> loader = ServiceLoader.load(CodecFactory.class); |
|
* Set<CodecFactory> pngFactories = loader |
|
* .stream() // Note a below |
|
* .filter(p -> p.type().isAnnotationPresent(PNG.class)) // Note b |
|
* .map(Provider::get) // Note c |
|
* .collect(Collectors.toSet()); |
|
* }</pre> |
|
* <ol type="a"> |
|
* <li> A stream of {@code Provider<CodecFactory>} objects </li> |
|
* <li> {@code p.type()} yields a {@code Class<CodecFactory>} </li> |
|
* <li> {@code get()} yields an instance of {@code CodecFactory} </li> |
|
* </ol> |
|
* |
|
* <h3> Designing services </h3> |
|
* |
|
* <p> A service is a single type, usually an interface or abstract class. A |
|
* concrete class can be used, but this is not recommended. The type may have |
|
* any accessibility. The methods of a service are highly domain-specific, so |
|
* this API specification cannot give concrete advice about their form or |
|
* function. However, there are two general guidelines: |
|
* <ol> |
|
* <li><p> A service should declare as many methods as needed to allow service |
|
* providers to communicate their domain-specific properties and other |
|
* quality-of-implementation factors. An application which obtains a service |
|
* loader for the service may then invoke these methods on each instance of |
|
* a service provider, in order to choose the best provider for the |
|
* application. </p></li> |
|
* <li><p> A service should express whether its service providers are intended |
|
* to be direct implementations of the service or to be an indirection |
|
* mechanism such as a "proxy" or a "factory". Service providers tend to be |
|
* indirection mechanisms when domain-specific objects are relatively |
|
* expensive to instantiate; in this case, the service should be designed |
|
* so that service providers are abstractions which create the "real" |
|
* implementation on demand. For example, the {@code CodecFactory} service |
|
* expresses through its name that its service providers are factories |
|
* for codecs, rather than codecs themselves, because it may be expensive |
|
* or complicated to produce certain codecs. </p></li> |
|
* </ol> |
|
* |
|
* <h3> <a id="developing-service-providers">Developing service providers</a> </h3> |
|
* |
|
* <p> A service provider is a single type, usually a concrete class. An |
|
* interface or abstract class is permitted because it may declare a static |
|
* provider method, discussed later. The type must be public and must not be |
|
* an inner class. |
|
* |
|
* <p> A service provider and its supporting code may be developed in a module, |
|
* which is then deployed on the application module path or in a modular |
|
* image. Alternatively, a service provider and its supporting code may be |
|
* packaged as a JAR file and deployed on the application class path. The |
|
* advantage of developing a service provider in a module is that the provider |
|
* can be fully encapsulated to hide all details of its implementation. |
|
* |
|
* <p> An application that obtains a service loader for a given service is |
|
* indifferent to whether providers of the service are deployed in modules or |
|
* packaged as JAR files. The application instantiates service providers via |
|
* the service loader's iterator, or via {@link Provider Provider} objects in |
|
* the service loader's stream, without knowledge of the service providers' |
|
* locations. |
|
* |
|
* <h3> Deploying service providers as modules </h3> |
|
* |
|
* <p> A service provider that is developed in a module must be specified in a |
|
* <i>provides</i> directive in the module declaration. The provides directive |
|
* specifies both the service and the service provider; this helps to locate the |
|
* provider when another module, with a <i>uses</i> directive for the service, |
|
* obtains a service loader for the service. It is strongly recommended that the |
|
* module does not export the package containing the service provider. There is |
|
* no support for a module specifying, in a <i>provides</i> directive, a service |
|
* provider in another module. |
|
|
|
* <p> A service provider that is developed in a module has no control over when |
|
* it is instantiated, since that occurs at the behest of the application, but it |
|
* does have control over how it is instantiated: |
|
* |
|
* <ul> |
|
* |
|
* <li> If the service provider declares a provider method, then the service |
|
* loader invokes that method to obtain an instance of the service provider. A |
|
* provider method is a public static method named "provider" with no formal |
|
* parameters and a return type that is assignable to the service's interface |
|
* or class. |
|
* <p> In this case, the service provider itself need not be assignable to the |
|
* service's interface or class. </li> |
|
* |
|
* <li> If the service provider does not declare a provider method, then the |
|
* service provider is instantiated directly, via its provider constructor. A |
|
* provider constructor is a public constructor with no formal parameters. |
|
* <p> In this case, the service provider must be assignable to the service's |
|
* interface or class </li> |
|
* |
|
* </ul> |
|
* |
|
* <p> A service provider that is deployed as an |
|
* {@linkplain java.lang.module.ModuleDescriptor#isAutomatic automatic module} on |
|
* the application module path must have a provider constructor. There is no |
|
* support for a provider method in this case. |
|
* |
|
* <p> As an example, suppose a module specifies the following directives: |
|
* <pre>{@code |
|
* provides com.example.CodecFactory with com.example.impl.StandardCodecs; |
|
* provides com.example.CodecFactory with com.example.impl.ExtendedCodecsFactory; |
|
* }</pre> |
|
* |
|
* <p> where |
|
* |
|
* <ul> |
|
* <li> {@code com.example.CodecFactory} is the two-method service from |
|
* earlier. </li> |
|
* |
|
* <li> {@code com.example.impl.StandardCodecs} is a public class that implements |
|
* {@code CodecFactory} and has a public no-args constructor. </li> |
|
* |
|
* <li> {@code com.example.impl.ExtendedCodecsFactory} is a public class that |
|
* does not implement CodecFactory, but it declares a public static no-args |
|
* method named "provider" with a return type of {@code CodecFactory}. </li> |
|
* </ul> |
|
* |
|
* <p> A service loader will instantiate {@code StandardCodecs} via its |
|
* constructor, and will instantiate {@code ExtendedCodecsFactory} by invoking |
|
* its {@code provider} method. The requirement that the provider constructor or |
|
* provider method is public helps to document the intent that the class (that is, |
|
* the service provider) will be instantiated by an entity (that is, a service |
|
* loader) which is outside the class's package. |
|
* |
|
* <h3> Deploying service providers on the class path </h3> |
|
* |
|
* A service provider that is packaged as a JAR file for the class path is |
|
* identified by placing a <i>provider-configuration file</i> in the resource |
|
* directory {@code META-INF/services}. The name of the provider-configuration |
|
* file is the fully qualified binary name of the service. The provider-configuration |
|
* file contains a list of fully qualified binary names of service providers, one |
|
* per line. |
|
* |
|
* <p> For example, suppose the service provider |
|
* {@code com.example.impl.StandardCodecs} is packaged in a JAR file for the |
|
* class path. The JAR file will contain a provider-configuration file named: |
|
* |
|
* <blockquote>{@code |
|
* META-INF/services/com.example.CodecFactory |
|
* }</blockquote> |
|
* |
|
* that contains the line: |
|
* |
|
* <blockquote>{@code |
|
* com.example.impl.StandardCodecs # Standard codecs |
|
* }</blockquote> |
|
* |
|
* <p><a id="format">The provider-configuration file must be encoded in UTF-8. </a> |
|
* Space and tab characters surrounding each service provider's name, as well as |
|
* blank lines, are ignored. The comment character is {@code '#'} |
|
* ({@code '\u0023'} <span style="font-size:smaller;">NUMBER SIGN</span>); |
|
* on each line all characters following the first comment character are ignored. |
|
* If a service provider class name is listed more than once in a |
|
* provider-configuration file then the duplicate is ignored. If a service |
|
* provider class is named in more than one configuration file then the duplicate |
|
* is ignored. |
|
* |
|
* <p> A service provider that is mentioned in a provider-configuration file may |
|
* be located in the same JAR file as the provider-configuration file or in a |
|
* different JAR file. The service provider must be visible from the class loader |
|
* that is initially queried to locate the provider-configuration file; this is |
|
* not necessarily the class loader which ultimately locates the |
|
* provider-configuration file. |
|
* |
|
* <h3> Timing of provider discovery </h3> |
|
* |
|
* <p> Service providers are loaded and instantiated lazily, that is, on demand. |
|
* A service loader maintains a cache of the providers that have been loaded so |
|
* far. Each invocation of the {@code iterator} method returns an {@code Iterator} |
|
* that first yields all of the elements cached from previous iteration, in |
|
* instantiation order, and then lazily locates and instantiates any remaining |
|
* providers, adding each one to the cache in turn. Similarly, each invocation |
|
* of the stream method returns a {@code Stream} that first processes all |
|
* providers loaded by previous stream operations, in load order, and then lazily |
|
* locates any remaining providers. Caches are cleared via the {@link #reload |
|
* reload} method. |
|
* |
|
* <h3> <a id="errors">Errors</a> </h3> |
|
* |
|
* <p> When using the service loader's {@code iterator}, the {@link |
|
* Iterator#hasNext() hasNext} and {@link Iterator#next() next} methods will |
|
* fail with {@link ServiceConfigurationError} if an error occurs locating, |
|
* loading or instantiating a service provider. When processing the service |
|
* loader's stream then {@code ServiceConfigurationError} may be thrown by any |
|
* method that causes a service provider to be located or loaded. |
|
* |
|
* <p> When loading or instantiating a service provider in a module, {@code |
|
* ServiceConfigurationError} can be thrown for the following reasons: |
|
* |
|
* <ul> |
|
* |
|
* <li> The service provider cannot be loaded. </li> |
|
* |
|
* <li> The service provider does not declare a provider method, and either |
|
* it is not assignable to the service's interface/class or does not have a |
|
* provider constructor. </li> |
|
* |
|
* <li> The service provider declares a public static no-args method named |
|
* "provider" with a return type that is not assignable to the service's |
|
* interface or class. </li> |
|
* |
|
* <li> The service provider class file has more than one public static |
|
* no-args method named "{@code provider}". </li> |
|
* |
|
* <li> The service provider declares a provider method and it fails by |
|
* returning {@code null} or throwing an exception. </li> |
|
* |
|
* <li> The service provider does not declare a provider method, and its |
|
* provider constructor fails by throwing an exception. </li> |
|
* |
|
* </ul> |
|
* |
|
* <p> When reading a provider-configuration file, or loading or instantiating |
|
* a provider class named in a provider-configuration file, then {@code |
|
* ServiceConfigurationError} can be thrown for the following reasons: |
|
* |
|
* <ul> |
|
* |
|
* <li> The format of the provider-configuration file violates the <a |
|
* href="ServiceLoader.html#format">format</a> specified above; </li> |
|
* |
|
* <li> An {@link IOException IOException} occurs while reading the |
|
* provider-configuration file; </li> |
|
* |
|
* <li> A service provider cannot be loaded; </li> |
|
* |
|
* <li> A service provider is not assignable to the service's interface or |
|
* class, or does not define a provider constructor, or cannot be |
|
* instantiated. </li> |
|
* |
|
* </ul> |
|
* |
|
* <h3> Security </h3> |
|
* |
|
* <p> Service loaders always execute in the security context of the caller |
|
* of the iterator or stream methods and may also be restricted by the security |
|
* context of the caller that created the service loader. |
|
* Trusted system code should typically invoke the methods in this class, and |
|
* the methods of the iterators which they return, from within a privileged |
|
* security context. |
|
* |
|
* <h3> Concurrency </h3> |
|
* |
|
* <p> Instances of this class are not safe for use by multiple concurrent |
|
* threads. |
|
* |
|
* <h3> Null handling </h3> |
|
* |
|
* <p> Unless otherwise specified, passing a {@code null} argument to any |
|
* method in this class will cause a {@link NullPointerException} to be thrown. |
|
* |
|
* @param <S> |
|
* The type of the service to be loaded by this loader |
|
* |
|
* @author Mark Reinhold |
|
* @since 1.6 |
|
* @revised 9 |
|
* @spec JPMS |
|
*/ |
|
|
|
public final class ServiceLoader<S> |
|
implements Iterable<S> |
|
{ |
|
|
|
private final Class<S> service; |
|
|
|
|
|
private final String serviceName; |
|
|
|
// The module layer used to locate providers; null when locating |
|
|
|
private final ModuleLayer layer; |
|
|
|
// The class loader used to locate, load, and instantiate providers; |
|
|
|
private final ClassLoader loader; |
|
|
|
|
|
private final AccessControlContext acc; |
|
|
|
|
|
private Iterator<Provider<S>> lookupIterator1; |
|
private final List<S> instantiatedProviders = new ArrayList<>(); |
|
|
|
|
|
private Iterator<Provider<S>> lookupIterator2; |
|
private final List<Provider<S>> loadedProviders = new ArrayList<>(); |
|
private boolean loadedAllProviders; |
|
|
|
|
|
private int reloadCount; |
|
|
|
private static JavaLangAccess LANG_ACCESS; |
|
static { |
|
LANG_ACCESS = SharedSecrets.getJavaLangAccess(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static interface Provider<S> extends Supplier<S> { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Class<? extends S> type(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Override S get(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private ServiceLoader(Class<?> caller, ModuleLayer layer, Class<S> svc) { |
|
Objects.requireNonNull(caller); |
|
Objects.requireNonNull(layer); |
|
Objects.requireNonNull(svc); |
|
checkCaller(caller, svc); |
|
|
|
this.service = svc; |
|
this.serviceName = svc.getName(); |
|
this.layer = layer; |
|
this.loader = null; |
|
this.acc = (System.getSecurityManager() != null) |
|
? AccessController.getContext() |
|
: null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private ServiceLoader(Class<?> caller, Class<S> svc, ClassLoader cl) { |
|
Objects.requireNonNull(svc); |
|
|
|
if (VM.isBooted()) { |
|
checkCaller(caller, svc); |
|
if (cl == null) { |
|
cl = ClassLoader.getSystemClassLoader(); |
|
} |
|
} else { |
|
|
|
// if we get here then it means that ServiceLoader is being used |
|
// before the VM initialization has completed. At this point then |
|
|
|
Module callerModule = caller.getModule(); |
|
Module base = Object.class.getModule(); |
|
Module svcModule = svc.getModule(); |
|
if (callerModule != base || svcModule != base) { |
|
fail(svc, "not accessible to " + callerModule + " during VM init"); |
|
} |
|
|
|
|
|
cl = null; |
|
} |
|
|
|
this.service = svc; |
|
this.serviceName = svc.getName(); |
|
this.layer = null; |
|
this.loader = cl; |
|
this.acc = (System.getSecurityManager() != null) |
|
? AccessController.getContext() |
|
: null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private ServiceLoader(Module callerModule, Class<S> svc, ClassLoader cl) { |
|
if (!callerModule.canUse(svc)) { |
|
fail(svc, callerModule + " does not declare `uses`"); |
|
} |
|
|
|
this.service = Objects.requireNonNull(svc); |
|
this.serviceName = svc.getName(); |
|
this.layer = null; |
|
this.loader = cl; |
|
this.acc = (System.getSecurityManager() != null) |
|
? AccessController.getContext() |
|
: null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static void checkCaller(Class<?> caller, Class<?> svc) { |
|
if (caller == null) { |
|
fail(svc, "no caller to check if it declares `uses`"); |
|
} |
|
|
|
|
|
Module callerModule = caller.getModule(); |
|
int mods = svc.getModifiers(); |
|
if (!Reflection.verifyMemberAccess(caller, svc, null, mods)) { |
|
fail(svc, "service type not accessible to " + callerModule); |
|
} |
|
|
|
// If the caller is in a named module then it should "uses" the |
|
|
|
if (!callerModule.canUse(svc)) { |
|
fail(svc, callerModule + " does not declare `uses`"); |
|
} |
|
} |
|
|
|
private static void fail(Class<?> service, String msg, Throwable cause) |
|
throws ServiceConfigurationError |
|
{ |
|
throw new ServiceConfigurationError(service.getName() + ": " + msg, |
|
cause); |
|
} |
|
|
|
private static void fail(Class<?> service, String msg) |
|
throws ServiceConfigurationError |
|
{ |
|
throw new ServiceConfigurationError(service.getName() + ": " + msg); |
|
} |
|
|
|
private static void fail(Class<?> service, URL u, int line, String msg) |
|
throws ServiceConfigurationError |
|
{ |
|
fail(service, u + ":" + line + ": " + msg); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private boolean inExplicitModule(Class<?> clazz) { |
|
Module module = clazz.getModule(); |
|
return module.isNamed() && !module.getDescriptor().isAutomatic(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Method findStaticProviderMethod(Class<?> clazz) { |
|
List<Method> methods = null; |
|
try { |
|
methods = LANG_ACCESS.getDeclaredPublicMethods(clazz, "provider"); |
|
} catch (Throwable x) { |
|
fail(service, "Unable to get public provider() method", x); |
|
} |
|
if (methods.isEmpty()) { |
|
|
|
return null; |
|
} |
|
|
|
|
|
Method result = null; |
|
for (Method method : methods) { |
|
int mods = method.getModifiers(); |
|
assert Modifier.isPublic(mods); |
|
if (Modifier.isStatic(mods)) { |
|
if (result != null) { |
|
fail(service, clazz + " declares more than one" |
|
+ " public static provider() method"); |
|
} |
|
result = method; |
|
} |
|
} |
|
if (result != null) { |
|
Method m = result; |
|
PrivilegedAction<Void> pa = () -> { |
|
m.setAccessible(true); |
|
return null; |
|
}; |
|
AccessController.doPrivileged(pa); |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Constructor<?> getConstructor(Class<?> clazz) { |
|
PrivilegedExceptionAction<Constructor<?>> pa |
|
= new PrivilegedExceptionAction<>() { |
|
@Override |
|
public Constructor<?> run() throws Exception { |
|
Constructor<?> ctor = clazz.getConstructor(); |
|
if (inExplicitModule(clazz)) |
|
ctor.setAccessible(true); |
|
return ctor; |
|
} |
|
}; |
|
Constructor<?> ctor = null; |
|
try { |
|
ctor = AccessController.doPrivileged(pa); |
|
} catch (Throwable x) { |
|
if (x instanceof PrivilegedActionException) |
|
x = x.getCause(); |
|
String cn = clazz.getName(); |
|
fail(service, cn + " Unable to get public no-arg constructor", x); |
|
} |
|
return ctor; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static class ProviderImpl<S> implements Provider<S> { |
|
final Class<S> service; |
|
final Class<? extends S> type; |
|
final Method factoryMethod; |
|
final Constructor<? extends S> ctor; |
|
final AccessControlContext acc; |
|
|
|
ProviderImpl(Class<S> service, |
|
Class<? extends S> type, |
|
Method factoryMethod, |
|
AccessControlContext acc) { |
|
this.service = service; |
|
this.type = type; |
|
this.factoryMethod = factoryMethod; |
|
this.ctor = null; |
|
this.acc = acc; |
|
} |
|
|
|
ProviderImpl(Class<S> service, |
|
Class<? extends S> type, |
|
Constructor<? extends S> ctor, |
|
AccessControlContext acc) { |
|
this.service = service; |
|
this.type = type; |
|
this.factoryMethod = null; |
|
this.ctor = ctor; |
|
this.acc = acc; |
|
} |
|
|
|
@Override |
|
public Class<? extends S> type() { |
|
return type; |
|
} |
|
|
|
@Override |
|
public S get() { |
|
if (factoryMethod != null) { |
|
return invokeFactoryMethod(); |
|
} else { |
|
return newInstance(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private S invokeFactoryMethod() { |
|
Object result = null; |
|
Throwable exc = null; |
|
if (acc == null) { |
|
try { |
|
result = factoryMethod.invoke(null); |
|
} catch (Throwable x) { |
|
exc = x; |
|
} |
|
} else { |
|
PrivilegedExceptionAction<?> pa = new PrivilegedExceptionAction<>() { |
|
@Override |
|
public Object run() throws Exception { |
|
return factoryMethod.invoke(null); |
|
} |
|
}; |
|
|
|
try { |
|
result = AccessController.doPrivileged(pa, acc); |
|
} catch (Throwable x) { |
|
if (x instanceof PrivilegedActionException) |
|
x = x.getCause(); |
|
exc = x; |
|
} |
|
} |
|
if (exc != null) { |
|
if (exc instanceof InvocationTargetException) |
|
exc = exc.getCause(); |
|
fail(service, factoryMethod + " failed", exc); |
|
} |
|
if (result == null) { |
|
fail(service, factoryMethod + " returned null"); |
|
} |
|
@SuppressWarnings("unchecked") |
|
S p = (S) result; |
|
return p; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private S newInstance() { |
|
S p = null; |
|
Throwable exc = null; |
|
if (acc == null) { |
|
try { |
|
p = ctor.newInstance(); |
|
} catch (Throwable x) { |
|
exc = x; |
|
} |
|
} else { |
|
PrivilegedExceptionAction<S> pa = new PrivilegedExceptionAction<>() { |
|
@Override |
|
public S run() throws Exception { |
|
return ctor.newInstance(); |
|
} |
|
}; |
|
|
|
try { |
|
p = AccessController.doPrivileged(pa, acc); |
|
} catch (Throwable x) { |
|
if (x instanceof PrivilegedActionException) |
|
x = x.getCause(); |
|
exc = x; |
|
} |
|
} |
|
if (exc != null) { |
|
if (exc instanceof InvocationTargetException) |
|
exc = exc.getCause(); |
|
String cn = ctor.getDeclaringClass().getName(); |
|
fail(service, |
|
"Provider " + cn + " could not be instantiated", exc); |
|
} |
|
return p; |
|
} |
|
|
|
// For now, equals/hashCode uses the access control context to ensure |
|
// that two Providers created with different contexts are not equal |
|
// when running with a security manager. |
|
|
|
@Override |
|
public int hashCode() { |
|
return Objects.hash(service, type, acc); |
|
} |
|
|
|
@Override |
|
public boolean equals(Object ob) { |
|
if (!(ob instanceof ProviderImpl)) |
|
return false; |
|
@SuppressWarnings("unchecked") |
|
ProviderImpl<?> that = (ProviderImpl<?>)ob; |
|
return this.service == that.service |
|
&& this.type == that.type |
|
&& Objects.equals(this.acc, that.acc); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Provider<S> loadProvider(ServiceProvider provider) { |
|
Module module = provider.module(); |
|
if (!module.canRead(service.getModule())) { |
|
|
|
return null; |
|
} |
|
|
|
String cn = provider.providerName(); |
|
Class<?> clazz = null; |
|
if (acc == null) { |
|
try { |
|
clazz = Class.forName(module, cn); |
|
} catch (LinkageError e) { |
|
fail(service, "Unable to load " + cn, e); |
|
} |
|
} else { |
|
PrivilegedExceptionAction<Class<?>> pa = () -> Class.forName(module, cn); |
|
try { |
|
clazz = AccessController.doPrivileged(pa); |
|
} catch (Throwable x) { |
|
if (x instanceof PrivilegedActionException) |
|
x = x.getCause(); |
|
fail(service, "Unable to load " + cn, x); |
|
return null; |
|
} |
|
} |
|
if (clazz == null) { |
|
fail(service, "Provider " + cn + " not found"); |
|
} |
|
|
|
int mods = clazz.getModifiers(); |
|
if (!Modifier.isPublic(mods)) { |
|
fail(service, clazz + " is not public"); |
|
} |
|
|
|
|
|
if (inExplicitModule(clazz)) { |
|
Method factoryMethod = findStaticProviderMethod(clazz); |
|
if (factoryMethod != null) { |
|
Class<?> returnType = factoryMethod.getReturnType(); |
|
if (!service.isAssignableFrom(returnType)) { |
|
fail(service, factoryMethod + " return type not a subtype"); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
Class<? extends S> type = (Class<? extends S>) returnType; |
|
return new ProviderImpl<S>(service, type, factoryMethod, acc); |
|
} |
|
} |
|
|
|
|
|
if (!service.isAssignableFrom(clazz)) { |
|
fail(service, clazz.getName() + " not a subtype"); |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
Class<? extends S> type = (Class<? extends S>) clazz; |
|
@SuppressWarnings("unchecked") |
|
Constructor<? extends S> ctor = (Constructor<? extends S> ) getConstructor(clazz); |
|
return new ProviderImpl<S>(service, type, ctor, acc); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private final class LayerLookupIterator<T> |
|
implements Iterator<Provider<T>> |
|
{ |
|
Deque<ModuleLayer> stack = new ArrayDeque<>(); |
|
Set<ModuleLayer> visited = new HashSet<>(); |
|
Iterator<ServiceProvider> iterator; |
|
|
|
Provider<T> nextProvider; |
|
ServiceConfigurationError nextError; |
|
|
|
LayerLookupIterator() { |
|
visited.add(layer); |
|
stack.push(layer); |
|
} |
|
|
|
private Iterator<ServiceProvider> providers(ModuleLayer layer) { |
|
ServicesCatalog catalog = LANG_ACCESS.getServicesCatalog(layer); |
|
return catalog.findServices(serviceName).iterator(); |
|
} |
|
|
|
@Override |
|
public boolean hasNext() { |
|
while (nextProvider == null && nextError == null) { |
|
|
|
while (iterator == null || !iterator.hasNext()) { |
|
|
|
if (stack.isEmpty()) |
|
return false; |
|
|
|
ModuleLayer layer = stack.pop(); |
|
List<ModuleLayer> parents = layer.parents(); |
|
for (int i = parents.size() - 1; i >= 0; i--) { |
|
ModuleLayer parent = parents.get(i); |
|
if (!visited.contains(parent)) { |
|
visited.add(parent); |
|
stack.push(parent); |
|
} |
|
} |
|
iterator = providers(layer); |
|
} |
|
|
|
|
|
ServiceProvider provider = iterator.next(); |
|
try { |
|
@SuppressWarnings("unchecked") |
|
Provider<T> next = (Provider<T>) loadProvider(provider); |
|
nextProvider = next; |
|
} catch (ServiceConfigurationError e) { |
|
nextError = e; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
@Override |
|
public Provider<T> next() { |
|
if (!hasNext()) |
|
throw new NoSuchElementException(); |
|
|
|
Provider<T> provider = nextProvider; |
|
if (provider != null) { |
|
nextProvider = null; |
|
return provider; |
|
} else { |
|
ServiceConfigurationError e = nextError; |
|
assert e != null; |
|
nextError = null; |
|
throw e; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final class ModuleServicesLookupIterator<T> |
|
implements Iterator<Provider<T>> |
|
{ |
|
ClassLoader currentLoader; |
|
Iterator<ServiceProvider> iterator; |
|
|
|
Provider<T> nextProvider; |
|
ServiceConfigurationError nextError; |
|
|
|
ModuleServicesLookupIterator() { |
|
this.currentLoader = loader; |
|
this.iterator = iteratorFor(loader); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private List<ServiceProvider> providers(ModuleLayer layer) { |
|
ServicesCatalog catalog = LANG_ACCESS.getServicesCatalog(layer); |
|
return catalog.findServices(serviceName); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private ClassLoader loaderFor(Module module) { |
|
SecurityManager sm = System.getSecurityManager(); |
|
if (sm == null) { |
|
return module.getClassLoader(); |
|
} else { |
|
PrivilegedAction<ClassLoader> pa = module::getClassLoader; |
|
return AccessController.doPrivileged(pa); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Iterator<ServiceProvider> iteratorFor(ClassLoader loader) { |
|
|
|
ServicesCatalog catalog; |
|
if (loader == null) { |
|
catalog = BootLoader.getServicesCatalog(); |
|
} else { |
|
catalog = ServicesCatalog.getServicesCatalogOrNull(loader); |
|
} |
|
List<ServiceProvider> providers; |
|
if (catalog == null) { |
|
providers = List.of(); |
|
} else { |
|
providers = catalog.findServices(serviceName); |
|
} |
|
|
|
|
|
ClassLoader platformClassLoader = ClassLoaders.platformClassLoader(); |
|
if (loader == null || loader == platformClassLoader) { |
|
return providers.iterator(); |
|
} else { |
|
List<ServiceProvider> allProviders = new ArrayList<>(providers); |
|
Iterator<ModuleLayer> iterator = LANG_ACCESS.layers(loader).iterator(); |
|
while (iterator.hasNext()) { |
|
ModuleLayer layer = iterator.next(); |
|
for (ServiceProvider sp : providers(layer)) { |
|
ClassLoader l = loaderFor(sp.module()); |
|
if (l != null && l != platformClassLoader) { |
|
allProviders.add(sp); |
|
} |
|
} |
|
} |
|
return allProviders.iterator(); |
|
} |
|
} |
|
|
|
@Override |
|
public boolean hasNext() { |
|
while (nextProvider == null && nextError == null) { |
|
|
|
while (!iterator.hasNext()) { |
|
if (currentLoader == null) { |
|
return false; |
|
} else { |
|
currentLoader = currentLoader.getParent(); |
|
iterator = iteratorFor(currentLoader); |
|
} |
|
} |
|
|
|
|
|
ServiceProvider provider = iterator.next(); |
|
try { |
|
@SuppressWarnings("unchecked") |
|
Provider<T> next = (Provider<T>) loadProvider(provider); |
|
nextProvider = next; |
|
} catch (ServiceConfigurationError e) { |
|
nextError = e; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
@Override |
|
public Provider<T> next() { |
|
if (!hasNext()) |
|
throw new NoSuchElementException(); |
|
|
|
Provider<T> provider = nextProvider; |
|
if (provider != null) { |
|
nextProvider = null; |
|
return provider; |
|
} else { |
|
ServiceConfigurationError e = nextError; |
|
assert e != null; |
|
nextError = null; |
|
throw e; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final class LazyClassPathLookupIterator<T> |
|
implements Iterator<Provider<T>> |
|
{ |
|
static final String PREFIX = "META-INF/services/"; |
|
|
|
Set<String> providerNames = new HashSet<>(); |
|
Enumeration<URL> configs; |
|
Iterator<String> pending; |
|
|
|
Provider<T> nextProvider; |
|
ServiceConfigurationError nextError; |
|
|
|
LazyClassPathLookupIterator() { } |
|
|
|
|
|
|
|
|
|
*/ |
|
private int parseLine(URL u, BufferedReader r, int lc, Set<String> names) |
|
throws IOException |
|
{ |
|
String ln = r.readLine(); |
|
if (ln == null) { |
|
return -1; |
|
} |
|
int ci = ln.indexOf('#'); |
|
if (ci >= 0) ln = ln.substring(0, ci); |
|
ln = ln.trim(); |
|
int n = ln.length(); |
|
if (n != 0) { |
|
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0)) |
|
fail(service, u, lc, "Illegal configuration-file syntax"); |
|
int cp = ln.codePointAt(0); |
|
if (!Character.isJavaIdentifierStart(cp)) |
|
fail(service, u, lc, "Illegal provider-class name: " + ln); |
|
int start = Character.charCount(cp); |
|
for (int i = start; i < n; i += Character.charCount(cp)) { |
|
cp = ln.codePointAt(i); |
|
if (!Character.isJavaIdentifierPart(cp) && (cp != '.')) |
|
fail(service, u, lc, "Illegal provider-class name: " + ln); |
|
} |
|
if (providerNames.add(ln)) { |
|
names.add(ln); |
|
} |
|
} |
|
return lc + 1; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private Iterator<String> parse(URL u) { |
|
Set<String> names = new LinkedHashSet<>(); |
|
try { |
|
URLConnection uc = u.openConnection(); |
|
uc.setUseCaches(false); |
|
try (InputStream in = uc.getInputStream(); |
|
BufferedReader r |
|
= new BufferedReader(new InputStreamReader(in, "utf-8"))) |
|
{ |
|
int lc = 1; |
|
while ((lc = parseLine(u, r, lc, names)) >= 0); |
|
} |
|
} catch (IOException x) { |
|
fail(service, "Error accessing configuration file", x); |
|
} |
|
return names.iterator(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private Class<?> nextProviderClass() { |
|
if (configs == null) { |
|
try { |
|
String fullName = PREFIX + service.getName(); |
|
if (loader == null) { |
|
configs = ClassLoader.getSystemResources(fullName); |
|
} else if (loader == ClassLoaders.platformClassLoader()) { |
|
// The platform classloader doesn't have a class path, |
|
|
|
if (BootLoader.hasClassPath()) { |
|
configs = BootLoader.findResources(fullName); |
|
} else { |
|
configs = Collections.emptyEnumeration(); |
|
} |
|
} else { |
|
configs = loader.getResources(fullName); |
|
} |
|
} catch (IOException x) { |
|
fail(service, "Error locating configuration files", x); |
|
} |
|
} |
|
while ((pending == null) || !pending.hasNext()) { |
|
if (!configs.hasMoreElements()) { |
|
return null; |
|
} |
|
pending = parse(configs.nextElement()); |
|
} |
|
String cn = pending.next(); |
|
try { |
|
return Class.forName(cn, false, loader); |
|
} catch (ClassNotFoundException x) { |
|
fail(service, "Provider " + cn + " not found"); |
|
return null; |
|
} |
|
} |
|
|
|
@SuppressWarnings("unchecked") |
|
private boolean hasNextService() { |
|
while (nextProvider == null && nextError == null) { |
|
try { |
|
Class<?> clazz = nextProviderClass(); |
|
if (clazz == null) |
|
return false; |
|
|
|
if (clazz.getModule().isNamed()) { |
|
|
|
continue; |
|
} |
|
|
|
if (service.isAssignableFrom(clazz)) { |
|
Class<? extends S> type = (Class<? extends S>) clazz; |
|
Constructor<? extends S> ctor |
|
= (Constructor<? extends S>)getConstructor(clazz); |
|
ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor, acc); |
|
nextProvider = (ProviderImpl<T>) p; |
|
} else { |
|
fail(service, clazz.getName() + " not a subtype"); |
|
} |
|
} catch (ServiceConfigurationError e) { |
|
nextError = e; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
private Provider<T> nextService() { |
|
if (!hasNextService()) |
|
throw new NoSuchElementException(); |
|
|
|
Provider<T> provider = nextProvider; |
|
if (provider != null) { |
|
nextProvider = null; |
|
return provider; |
|
} else { |
|
ServiceConfigurationError e = nextError; |
|
assert e != null; |
|
nextError = null; |
|
throw e; |
|
} |
|
} |
|
|
|
@Override |
|
public boolean hasNext() { |
|
if (acc == null) { |
|
return hasNextService(); |
|
} else { |
|
PrivilegedAction<Boolean> action = new PrivilegedAction<>() { |
|
public Boolean run() { return hasNextService(); } |
|
}; |
|
return AccessController.doPrivileged(action, acc); |
|
} |
|
} |
|
|
|
@Override |
|
public Provider<T> next() { |
|
if (acc == null) { |
|
return nextService(); |
|
} else { |
|
PrivilegedAction<Provider<T>> action = new PrivilegedAction<>() { |
|
public Provider<T> run() { return nextService(); } |
|
}; |
|
return AccessController.doPrivileged(action, acc); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private Iterator<Provider<S>> newLookupIterator() { |
|
assert layer == null || loader == null; |
|
if (layer != null) { |
|
return new LayerLookupIterator<>(); |
|
} else { |
|
Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>(); |
|
Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>(); |
|
return new Iterator<Provider<S>>() { |
|
@Override |
|
public boolean hasNext() { |
|
return (first.hasNext() || second.hasNext()); |
|
} |
|
@Override |
|
public Provider<S> next() { |
|
if (first.hasNext()) { |
|
return first.next(); |
|
} else if (second.hasNext()) { |
|
return second.next(); |
|
} else { |
|
throw new NoSuchElementException(); |
|
} |
|
} |
|
}; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Iterator<S> iterator() { |
|
|
|
|
|
if (lookupIterator1 == null) { |
|
lookupIterator1 = newLookupIterator(); |
|
} |
|
|
|
return new Iterator<S>() { |
|
|
|
|
|
final int expectedReloadCount = ServiceLoader.this.reloadCount; |
|
|
|
|
|
int index; |
|
|
|
|
|
|
|
|
|
*/ |
|
private void checkReloadCount() { |
|
if (ServiceLoader.this.reloadCount != expectedReloadCount) |
|
throw new ConcurrentModificationException(); |
|
} |
|
|
|
@Override |
|
public boolean hasNext() { |
|
checkReloadCount(); |
|
if (index < instantiatedProviders.size()) |
|
return true; |
|
return lookupIterator1.hasNext(); |
|
} |
|
|
|
@Override |
|
public S next() { |
|
checkReloadCount(); |
|
S next; |
|
if (index < instantiatedProviders.size()) { |
|
next = instantiatedProviders.get(index); |
|
} else { |
|
next = lookupIterator1.next().get(); |
|
instantiatedProviders.add(next); |
|
} |
|
index++; |
|
return next; |
|
} |
|
|
|
}; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Stream<Provider<S>> stream() { |
|
|
|
if (loadedAllProviders) { |
|
return loadedProviders.stream(); |
|
} |
|
|
|
|
|
if (lookupIterator2 == null) { |
|
lookupIterator2 = newLookupIterator(); |
|
} |
|
|
|
|
|
Spliterator<Provider<S>> s = new ProviderSpliterator<>(lookupIterator2); |
|
return StreamSupport.stream(s, false); |
|
} |
|
|
|
private class ProviderSpliterator<T> implements Spliterator<Provider<T>> { |
|
final int expectedReloadCount = ServiceLoader.this.reloadCount; |
|
final Iterator<Provider<T>> iterator; |
|
int index; |
|
|
|
ProviderSpliterator(Iterator<Provider<T>> iterator) { |
|
this.iterator = iterator; |
|
} |
|
|
|
@Override |
|
public Spliterator<Provider<T>> trySplit() { |
|
return null; |
|
} |
|
|
|
@Override |
|
@SuppressWarnings("unchecked") |
|
public boolean tryAdvance(Consumer<? super Provider<T>> action) { |
|
if (ServiceLoader.this.reloadCount != expectedReloadCount) |
|
throw new ConcurrentModificationException(); |
|
Provider<T> next = null; |
|
if (index < loadedProviders.size()) { |
|
next = (Provider<T>) loadedProviders.get(index++); |
|
} else if (iterator.hasNext()) { |
|
next = iterator.next(); |
|
loadedProviders.add((Provider<S>)next); |
|
index++; |
|
} else { |
|
loadedAllProviders = true; |
|
} |
|
if (next != null) { |
|
action.accept(next); |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
@Override |
|
public int characteristics() { |
|
// not IMMUTABLE as structural interference possible |
|
// not NOTNULL so that the characteristics are a subset of the |
|
|
|
return Spliterator.ORDERED; |
|
} |
|
|
|
@Override |
|
public long estimateSize() { |
|
return Long.MAX_VALUE; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static <S> ServiceLoader<S> load(Class<S> service, |
|
ClassLoader loader, |
|
Module callerModule) |
|
{ |
|
return new ServiceLoader<>(callerModule, service, loader); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@CallerSensitive |
|
public static <S> ServiceLoader<S> load(Class<S> service, |
|
ClassLoader loader) |
|
{ |
|
return new ServiceLoader<>(Reflection.getCallerClass(), service, loader); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@CallerSensitive |
|
public static <S> ServiceLoader<S> load(Class<S> service) { |
|
ClassLoader cl = Thread.currentThread().getContextClassLoader(); |
|
return new ServiceLoader<>(Reflection.getCallerClass(), service, cl); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@CallerSensitive |
|
public static <S> ServiceLoader<S> loadInstalled(Class<S> service) { |
|
ClassLoader cl = ClassLoader.getPlatformClassLoader(); |
|
return new ServiceLoader<>(Reflection.getCallerClass(), service, cl); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@CallerSensitive |
|
public static <S> ServiceLoader<S> load(ModuleLayer layer, Class<S> service) { |
|
return new ServiceLoader<>(Reflection.getCallerClass(), layer, service); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Optional<S> findFirst() { |
|
Iterator<S> iterator = iterator(); |
|
if (iterator.hasNext()) { |
|
return Optional.of(iterator.next()); |
|
} else { |
|
return Optional.empty(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void reload() { |
|
lookupIterator1 = null; |
|
instantiatedProviders.clear(); |
|
|
|
lookupIterator2 = null; |
|
loadedProviders.clear(); |
|
loadedAllProviders = false; |
|
|
|
|
|
reloadCount++; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String toString() { |
|
return "java.util.ServiceLoader[" + service.getName() + "]"; |
|
} |
|
|
|
} |