All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javax0.jamal.api.ServiceLoaded Maven / Gradle / Ivy

package javax0.jamal.api;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;

/**
 * This helper interface can load classes that implement a specific interface using the service loader. If the service
 * loader does not see the classes (the program sees that zero service instances were loaded) then as a fallback it tries
 * to load the {@code META-INF/services/className} files and the classes listed in those.
 * 

* The Javadoc Taglet execution environment for some reason does not load the service loader loaded instances. * This is a workaround. *

* The {@link Macro} interface extends this interface, because macros are service loaded. *

* This is a very general interface, could be used in any other project, nothing specific to Jamal. */ public interface ServiceLoaded { /** * Load the classes that implement the interface {@code klass} and are provided by the modules or are available. * * @param klass the interface for which the implementing class instances are needed * @param the interface * @return the list of instances */ static List getInstances(Class klass) { final var services = getInstances(klass, Thread.currentThread().getContextClassLoader()); if( services.size() > 0 ){ return services; } return getInstances(klass, ServiceLoaded.class.getClassLoader()); } static List getInstances(Class klass, final ClassLoader cl) { List list = new ArrayList<>(); try { final ServiceLoader services = ServiceLoader.load(klass, cl); services.iterator().forEachRemaining(list::add); if (list.size() == 0) { loadViaMetaInf(klass, list, cl); } return list; } catch (ServiceConfigurationError ignored) { loadViaMetaInf(klass, list, cl); return list; } } /** * Load the classes using the names listed in the {@code META-INF/services/}class name resources found by the * classloader. *

* The implementation mimics the behaviour of the class loader using the {@code provider()} public static method if * it exists in the implementation. *

* Class types and assignability is not checked by the method. If there is any discrepancy a class cast exception * will occur. * * @param klass the interface for which the classes are to be loaded * @param list the list to fill the instances to * @param the klass type */ static void loadViaMetaInf(final Class klass, final List list, final ClassLoader cl) { try { final var classes = new HashSet>(); // different classloaders in the hierarchy may load the same file more than once for (final var url : loadResources("META-INF/services/" + klass.getName(), cl)) { try (var is = url.openStream()) { for (final var className : new String(is.readAllBytes(), StandardCharsets.UTF_8).split("[\n\r]+")) { try { final var providerKlass = (Class) Class.forName(className); if (!classes.contains(providerKlass)) { classes.add(providerKlass); final Method providerMethod = getProvider(providerKlass); final T instance; if (providerMethod == null) { instance = providerKlass.getConstructor().newInstance(); } else { instance = (T) providerMethod.invoke(null); } list.add(instance); } } catch (ClassCastException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { // ignored, here we try our best } } } } } catch (IOException e) { //ignored } } /** * Get the provider method from the class. The {@code provider()} is a public static method in the class. If it * exists it has to return an instance of the serviced class. In this case the class {@code klass} does not even * need to implement the service interface. * * @param klass the provider class * @param the klass type * @return the provider method or {@code null} if there is no provider method in the class */ private static Method getProvider(Class klass) { try { return klass.getDeclaredMethod("provider"); } catch (NoClassDefFoundError | NoSuchMethodException e) { return null; } } /** * Get the url list of the resources for the given name. * * @param name the name of the resource * @param classLoader the class loader to use or {@code null} to use the system class loader * @return the list of the urls * @throws IOException */ static List loadResources(String name, ClassLoader classLoader) throws IOException { final List list = new ArrayList<>(); final Enumeration systemResources = (classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader) .getResources(name); while (systemResources.hasMoreElements()) { list.add(systemResources.nextElement()); } return list; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy