001package javax.visrec.spi;
002
003import java.util.*;
004
005/*
006 * Special thanks to Werner Keil and Martin Desruisseaux for the proven design of a service provider.
007 * The ServiceProvider of JSR381 is heavily inspired by JSR 363/385.
008 */
009
010/**
011 * The ServiceProvider is the centralized provider to provide API scoped services.
012 *
013 * @author Werner Keil
014 * @author Martin Desruisseaux
015 * @author Kevin Berendsen
016 * @since 1.0
017 */
018public abstract class ServiceProvider {
019
020    /** The lock to populate and mutate the providers list. */
021    private static final Object LOCK = new Object();
022
023    private static List<ServiceProvider> providers;
024
025    protected ServiceProvider() {
026        // Prevent instantiation, only allowed by subclasses.
027    }
028
029    /**
030     * If multiple implementations of the {@link ServiceProvider} are found on the classpath, then the
031     * {@link ServiceProvider} with the highest value of priority will be used by default.
032     * @return The priority (default = 0)
033     */
034    public int getPriority() {
035        return 0;
036    }
037
038    /**
039     * Get the {@link BuilderService}
040     * @return builder service.
041     */
042    public abstract BuilderService getBuilderService();
043
044    /**
045     * Get the {@link ClassifierService}
046     * @return classifier service
047     */
048    public abstract ClassifierService getClassifierService();
049
050    /**
051     * Get the {@link ImageFactoryService}
052     * @return image factory service.
053     */
054    public abstract ImageFactoryService getImageFactoryService();
055
056    /**
057     * Get the {@link ImplementationService}
058     * @return implementation service.
059     */
060    public abstract ImplementationService getImplementationService();
061
062    /**
063     * Get the current {@link ServiceProvider}
064     * @return service provider.
065     * @throws IllegalStateException If there are no service providers found.
066     */
067    public static ServiceProvider current() {
068        if (available().size() == 0) {
069            throw new IllegalStateException("No service provider found");
070        }
071        return available().get(0);
072    }
073
074    /**
075     * Set the current {@link ServiceProvider}
076     * @param provider The {@link ServiceProvider} to be set as current.
077     * @throws IllegalStateException If there are no service providers found.
078     * @throws IllegalArgumentException If the {@link ServiceProvider} given by the parameters is not known
079     * in the existing list of providers.
080     */
081    public static void setCurrent(final ServiceProvider provider) {
082        Objects.requireNonNull(provider);
083
084        synchronized (LOCK) {
085            final List<ServiceProvider> foundProviders = available();
086            if (foundProviders.isEmpty()) {
087                throw new IllegalStateException("No providers found.");
088            }
089            if (!foundProviders.contains(provider)) {
090                throw new IllegalArgumentException("ServiceProvider given through the parameters is not known.");
091            }
092
093            // Copying list, removes the provider from the arguments and prepends it upfront
094            // on the copied list.
095            final ArrayList<ServiceProvider> copiedProviders = new ArrayList<>();
096            Collections.copy(copiedProviders, foundProviders);
097            copiedProviders.remove(provider);
098            copiedProviders.add(0, provider);
099            copiedProviders.trimToSize();
100
101            // Make the list unmodifiable to prevent illegal modification
102            providers = Collections.unmodifiableList(copiedProviders);
103        }
104    }
105
106    /**
107     * Gets a list of all available {@link ServiceProvider}s.
108     * @return service providers.
109     */
110    public static List<ServiceProvider> available() {
111        if (Objects.isNull(providers)) {
112            synchronized(LOCK) {
113                if (Objects.nonNull(providers)) {
114                    return providers;
115                }
116
117                // Searches for implementations of the ServiceProvider using the ServiceLoader API
118                final ServiceLoader<ServiceProvider> serviceLoader = ServiceLoader.load(ServiceProvider.class);
119                final ArrayList<ServiceProvider> localProviders = new ArrayList<>();
120                for (ServiceProvider provider : serviceLoader) {
121                    localProviders.add(provider);
122                }
123                localProviders.trimToSize();
124
125                // Sort the list based on priority. Highest priority comes first.
126                localProviders.sort((p1, p2) -> p2.getPriority() - p1.getPriority());
127
128                // Make the list unmodifiable to prevent illegal modification
129                providers = Collections.unmodifiableList(localProviders);
130            }
131        }
132        return providers;
133    }
134
135}