Proposal: #ServiceLoaderEnhancements
Stephen Colebourne
scolebourne at joda.org
Mon Sep 12 22:08:41 UTC 2016
My preference of these three options is option 2. However, I would
prefer to see the method name be defined by the module definition as
previously suggested [1]:
module {
provides java.sql.Driver with com.mysql.jdbc.Driver::instance;
}
This would allow ::staticMethod or ::staticConstant, and avoid
hard-coding "provider" as a special method name.
(For my motivating use case, the Chronology classes of ThreeTen-Extra
[2] it would be distracting to have a method named provider(). While a
separate class in a non-exported package would be possible to hold the
provider() method, as a solution that seems like overkill relative to
the syntax above.)
Stephen
[1] http://mail.openjdk.java.net/pipermail/jpms-spec-observers/2016-July/000535.html
[2] https://github.com/ThreeTen/threeten-extra/tree/master/src/main/java/org/threeten/extra/chrono
On 12 September 2016 at 16:13, Mark Reinhold <mark.reinhold at oracle.com> wrote:
> Issue summary
> -------------
>
> #ServiceLoaderEnhancements --- The module system encourages the use of
> services for loose coupling, but the `ServiceLoader` class is not very
> flexible. Consider enhancing it so that (1) neither a provider class
> nor its no-args constructor need be declared `public`, (2) a provider
> can be a singleton, or perhaps a collection of singletons, and (3) the
> classes of the available providers can be inspected and selected prior
> to instantiation. [13]
>
> Proposal
> --------
>
> (1) No change: Continue to require service-provider classes, and their
> no-args constructors, to be public.
>
> Providers on the class path, and their no-args constructors, must
> always be public. Allowing a class-path provider or its no-args
> constructor to be non-public introduces a security risk, since an
> adversary could place a `META-INF/services` entry elsewhere on the
> class path in order to force that otherwise-inaccessible
> constructor to be invoked.
>
> For providers in named modules, allowing non-public provider
> classes and non-public no-args constructors isn't really necessary
> and is, in some ways, counterproductive. In a named module a
> provider class, and its constructor, can be encapsulated by placing
> the provider in an unexported package. Having to declare the
> provider class and its no-args constructor `public` is a useful
> declaration of intent, since they will be accessed by the service
> loader itself, and hence useful documentation.
>
> (2) Revise the `ServiceLoader` class so that if a candidate provider
> class in a named module has a no-args public static method named
> `provider` then that method is invoked and its result is taken as
> the provider object. An exception is thrown if the method does
> not have an appropriate return type. A static `provider` method
> can either return a singleton or act as a factory method. If the
> candidate provider class does not have such a method then its
> public no-args constructor, if any, is invoked, per (1) above.
>
> (An alternative is to use an annotation, say `@Provider`, to
> identify provider-containing fields or provider-returning methods.
> The cost of loading the annotation-reading code into the JVM is,
> however, nontrivial, and since services are used widely within the
> JDK itself we'd prefer not to impose that overhead on all
> applications.)
>
> (3) Decouple the loading of provider classes from the instantiation of
> such classes: Introduce a new `ServiceLoader.Provider` interface
> that pairs a provider class with a method to instantiate that
> provider, and add a `stream()` method that returns a stream of
> objects implementing that interface. A client can then filter
> providers by inspecting the elements of the stream, examining each
> provider class and perhaps the annotations thereon, and then
> instantiating the class if appropriate. (Draft Javadoc source
> attached below.)
>
> [1] http://openjdk.java.net/projects/jigsaw/spec/issues/#ServiceLoaderEnhancements
>
> --
>
> /**
> * Represents a service provider located by {@code ServiceLoader}.
> *
> * <p> When using a loader's {@link ServiceLoader#stream() stream()} method
> * then the elements are of type {@code Provider}. This allows processing
> * to select or filter on the provider class without instantiating the
> * provider. </p>
> *
> * @param <S> The service type
> * @since 9
> */
> public static interface Provider<S> extends Supplier<S> {
>
> /**
> * Returns the provider class. There is no guarantee that this type is
> * accessible and so attempting to instantiate it, by means of its
> * {@link Class#newInstance() newInstance()} method for example, will
> * fail when it is not accessible. The {@link #get() get()} method
> * should instead be used to obtain the provider.
> *
> * @return The provider class
> */
> Class<S> type();
>
> /**
> * Returns an instance of the provider.
> *
> * @return An instance of the provider.
> *
> * @throws ServiceConfigurationError
> * If the service provider cannot be instantiated. The error
> * cause will carry an appropriate cause.
> */
> @Override S get();
>
> }
>
> /**
> * Returns a stream that lazily loads the available providers of this
> * loader's service. The stream elements are of type {@link Provider
> * Provider}, the {@code Provider}'s {@link Provider#get() get} method
> * must be invoked to get or instantiate the provider.
> *
> * <p> When processing the stream then providers that were previously
> * loaded by stream operations are processed first, in load order. It then
> * lazily loads any remaining providers. If a provider class cannot be
> * loaded, can't be assigned to the service type, or some other error is
> * thrown when locating the provider then it is wrapped with a {@code
> * ServiceConfigurationError} and thrown by whatever method caused the
> * provider to be loaded. </p>
> *
> * <p> If this loader's provider caches are cleared by invoking the {@link
> * #reload() reload} method then existing streams for this service
> * loader should be discarded. </p>
> *
> * <p> The following examples demonstrate usage. The first example
> * creates a stream of providers, the second example is the same except
> * that it sorts the providers by provider class name (and so locate all
> * providers).
> * <pre>{@code
> * Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
> * .stream()
> * .map(Provider::get);
> *
> * Stream<CodecSet> providers = ServiceLoader.load(CodecSet.class)
> * .stream()
> * .sorted(Comparator.comparing(p -> p.type().getName()))
> * .map(Provider::get);
> * }</pre>
> *
> * @return A stream that lazily loads providers for this loader's service
> *
> * @since 9
> */
> public Stream<Provider<S>> stream() { ... }
More information about the jigsaw-dev
mailing list