
org.ocpsoft.common.services.ServiceLoader Maven / Gradle / Ivy
The newest version!
/* * Copyright 2011 Lincoln Baxter, III * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ocpsoft.common.services; 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.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.ocpsoft.common.spi.ServiceEnricher; import org.ocpsoft.common.spi.ServiceLocator; /** * This class handles looking up service providers on the class path. It implements the * Service Provider * section of the JAR File Specification. * * The Service Provider programmatic lookup was not specified prior to Java 6 so this interface allows use of the * specification prior to Java 6. * * The API is copied from * java.util.ServiceLoader * * @author Pete Muir * @author Avalon Development Team * @author Nicklas Karlsson * @author Lincoln Baxter, III */ public class ServiceLoader
} * * is equivalent to * *implements Iterable{ private static final String SERVICES = "META-INF/services"; /** * Creates a new service loader for the given service type, using the current thread's context class loader. * * An invocation of this convenience method of the form * * {@code ServiceLoader.load(service)ServiceLoader.load(service, * Thread.currentThread().getContextClassLoader())
* * @param service The interface or abstract class representing the service * @return A new service loader */ @SuppressWarnings("rawtypes") public staticServiceLoader load(final Classservice) { return load(service, Thread.currentThread().getContextClassLoader()); } /** * Creates a new service loader for the given service type, using the current thread's context class loader. * * An invocation of this convenience method of the form * * {@code ServiceLoader.load(service)} * * is equivalent to * *ServiceLoader.load(service, * Thread.currentThread().getContextClassLoader())
* * @param service The interface or abstract class representing the service * @return A new service loader */ public staticServiceLoaderloadTypesafe(final Classservice) { return load(service, Thread.currentThread().getContextClassLoader()); } /** * Creates a new service loader for the given service type and class loader. * * @param service The interface or abstract class representing the service * @param loader The class loader to be used to load provider-configuration files and provider classes, or null if * the system class loader (or, failing that, the bootstrap class loader) is to be used * @return A new service loader */ public staticServiceLoaderload(final Classservice, ClassLoader loader) { if (loader == null) { loader = service.getClassLoader(); } return new ServiceLoader<>(service, loader); } private final String serviceFile; private final ClassexpectedType; private final ClassLoader loader; private Setproviders; private Set> loadedImplementations; private java.util.ServiceLoader locatorLoader; private ServiceLoader(final Class service, final ClassLoader loader) { this.loader = loader; this.serviceFile = SERVICES + "/" + service.getName(); this.expectedType = service; } /** * Clear this loader's provider cache so that all providers will be reloaded. * * After invoking this method, subsequent invocations of the iterator method will lazily look up and instantiate * providers from scratch, just as is done by a newly-created loader. * * This method is intended for use in situations in which new providers can be installed into a running Java virtual * machine. */ public void reload() { providers = new HashSet<>(); loadedImplementations = new HashSet<>(); for (URL serviceFile : loadServiceFiles()) { loadServiceFile(serviceFile); } if (locatorLoader == null) locatorLoader = java.util.ServiceLoader .load(ServiceLocator.class); for (ServiceLocator locator : locatorLoader) { Collection> serviceTypes = locator.locate(expectedType); if ((serviceTypes != null) && !serviceTypes.isEmpty()) { for (Class type : serviceTypes) { loadClass(type); } } } } private ListloadServiceFiles() { List serviceFiles = new ArrayList<>(); try { Enumeration serviceFileEnumerator = loader.getResources(serviceFile); while (serviceFileEnumerator.hasMoreElements()) { serviceFiles.add(serviceFileEnumerator.nextElement()); } } catch (IOException e) { throw new RuntimeException("Could not load resources from " + serviceFile, e); } return serviceFiles; } private void loadServiceFile(final URL serviceFile) { try (InputStream is = serviceFile.openStream()) { BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); String serviceClassName = null; while ((serviceClassName = reader.readLine()) != null) { serviceClassName = trim(serviceClassName); if (serviceClassName.length() > 0) { loadService(serviceClassName); } } } catch (IOException e) { throw new RuntimeException("Could not read services file " + serviceFile, e); } } private String trim(String line) { final int comment = line.indexOf('#'); if (comment > -1) { line = line.substring(0, comment); } return line.trim(); } private void loadService(final String serviceClassName) { Class extends S> serviceClass = loadClass(serviceClassName); loadClass(serviceClass); } private void loadClass(final Class extends S> serviceClass) { if (serviceClass == null) { return; } if (loadedImplementations.contains(serviceClass)) { return; } Collection extends S> services = loadEnriched(serviceClass); if ((services == null) || services.isEmpty()) { return; } for (S service : services) { loadedImplementations.add(service.getClass()); } providers.addAll(services); } private Class extends S> loadClass(final String serviceClassName) { Class> clazz = null; Class extends S> serviceClass = null; try { clazz = loader.loadClass(serviceClassName); serviceClass = clazz.asSubclass(expectedType); } catch (ClassNotFoundException e) { throw new RuntimeException("ClassNotFoundException: Service class [" + serviceClassName + "] could not be loaded."); } catch (ClassCastException e) { throw new ClassCastException("ClassCastException: Service class [" + serviceClassName + "] did not implement the interface [" + expectedType.getName() + "]"); } return serviceClass; } private static java.util.ServiceLoader enricherLoader = null; /** * Obtain a {@link Class} instance and attempt enrichment using any provided {@link ServiceEnricher} classes. * * @author Lincoln Baxter, III */ public static Collection loadEnriched(final Class serviceClass) { try { Collection services = Collections.emptyList(); ServiceEnricher origin = null; if (!NonEnriching.class.isAssignableFrom(serviceClass)) { if (enricherLoader == null) { enricherLoader = java.util.ServiceLoader .load(ServiceEnricher.class); } for (ServiceEnricher enricher : enricherLoader) { services = enricher.produce(serviceClass); if (services == null) { services = Collections.emptyList(); } if (!services.isEmpty()) { origin = enricher; break; } } } if (services.isEmpty()) { Constructor extends T> constructor = serviceClass.getDeclaredConstructor(); constructor.setAccessible(true); services = Collections.singletonList(constructor.newInstance()); } if (!NonEnriching.class.isAssignableFrom(serviceClass)) { for (ServiceEnricher enricher : enricherLoader) { if (!enricher.equals(origin)) { for (T service : services) { enricher.enrich(service); } } } } return services; } catch (NoClassDefFoundError e) { throw new RuntimeException("Could not instantiate service class " + serviceClass.getName(), e); } catch (InvocationTargetException e) { throw new RuntimeException("Error instantiating " + serviceClass, e.getCause()); } catch (IllegalArgumentException | InstantiationException | IllegalAccessException | SecurityException | NoSuchMethodException e) { throw new RuntimeException("Error instantiating " + serviceClass, e); } } /** * Lazily loads the available providers of this loader's service. * * The iterator returned by this method first yields all of the elements of the provider cache, in instantiation * order. It then lazily loads and instantiates any remaining providers, adding each one to the cache in turn. * * To achieve laziness the actual work of parsing the available provider-configuration files and instantiating * providers must be done by the iterator itself. Its hasNext and next methods can therefore throw a * ServiceConfigurationError if a provider-configuration file violates the specified format, or if it names a * provider class that cannot be found and instantiated, or if the result of instantiating the class is not * assignable to the service type, or if any other kind of exception or error is thrown as the next provider is * located and instantiated. To write robust code it is only necessary to catch ServiceConfigurationError when using * a service iterator. * * If such an error is thrown then subsequent invocations of the iterator will make a best effort to locate and * instantiate the next available provider, but in general such recovery cannot be guaranteed. * * Design Note Throwing an error in these cases may seem extreme. The rationale for this behavior is that a malformed * provider-configuration file, like a malformed class file, indicates a serious problem with the way the Java * virtual machine is configured or is being used. As such it is preferable to throw an error rather than try to * recover or, even worse, fail silently. * * The iterator returned by this method does not support removal. Invoking its remove method will cause an * UnsupportedOperationException to be thrown. * * @return An iterator that lazily loads providers for this loader's service */ @Override public Iterator iterator() { if (providers == null) { reload(); } return providers.iterator(); } /** * Returns a string describing this service. * * @return A descriptive string */ @Override public String toString() { return "Services for " + serviceFile; } }
© 2015 - 2025 Weber Informatics LLC | Privacy Policy