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     * @Deprecated for removal
042     */
043    @Deprecated
044    public abstract BuilderService getBuilderService();
045
046    /**
047     * Get the {@link ClassifierFactoryService}
048     * @return classifier creator service
049     */
050    public ClassifierFactoryService getClassifierFactoryService() {
051        return ClassifierFactoryService.getInstance();
052    }
053
054    /**
055     * Get the {@link ImageFactoryService}
056     * @return image factory service.
057     */
058    public abstract ImageFactoryService getImageFactoryService();
059
060    /**
061     * Get the {@link ImplementationService}
062     * @return implementation service.
063     */
064    public abstract ImplementationService getImplementationService();
065
066    /**
067     * Get the current {@link ServiceProvider}
068     * @return service provider.
069     * @throws IllegalStateException If there are no service providers found.
070     */
071    public static ServiceProvider current() {
072        if (available().size() == 0) {
073            throw new IllegalStateException("No service provider found");
074        }
075        return available().get(0);
076    }
077
078    /**
079     * Set the current {@link ServiceProvider}
080     * @param provider The {@link ServiceProvider} to be set as current.
081     * @throws IllegalStateException If there are no service providers found.
082     * @throws IllegalArgumentException If the {@link ServiceProvider} given by the parameters is not known
083     * in the existing list of providers.
084     */
085    public static void setCurrent(final ServiceProvider provider) {
086        Objects.requireNonNull(provider);
087
088        synchronized (LOCK) {
089            final List<ServiceProvider> foundProviders = available();
090            if (foundProviders.isEmpty()) {
091                throw new IllegalStateException("No providers found.");
092            }
093            if (!foundProviders.contains(provider)) {
094                throw new IllegalArgumentException("ServiceProvider given through the parameters is not known.");
095            }
096
097            // Copying list, removes the provider from the arguments and prepends it upfront
098            // on the copied list.
099            final ArrayList<ServiceProvider> copiedProviders = new ArrayList<>();
100            Collections.copy(copiedProviders, foundProviders);
101            copiedProviders.remove(provider);
102            copiedProviders.add(0, provider);
103            copiedProviders.trimToSize();
104
105            // Make the list unmodifiable to prevent illegal modification
106            providers = Collections.unmodifiableList(copiedProviders);
107        }
108    }
109
110    /**
111     * Gets a list of all available {@link ServiceProvider}s.
112     * @return service providers.
113     */
114    public static List<ServiceProvider> available() {
115        if (Objects.isNull(providers)) {
116            synchronized(LOCK) {
117                if (Objects.nonNull(providers)) {
118                    return providers;
119                }
120
121                // Searches for implementations of the ServiceProvider using the ServiceLoader API
122                final ServiceLoader<ServiceProvider> serviceLoader = ServiceLoader.load(ServiceProvider.class);
123                final ArrayList<ServiceProvider> localProviders = new ArrayList<>();
124                for (ServiceProvider provider : serviceLoader) {
125                    localProviders.add(provider);
126                }
127                localProviders.trimToSize();
128
129                // Sort the list based on priority. Highest priority comes first.
130                localProviders.sort((p1, p2) -> p2.getPriority() - p1.getPriority());
131
132                // Make the list unmodifiable to prevent illegal modification
133                providers = Collections.unmodifiableList(localProviders);
134            }
135        }
136        return providers;
137    }
138
139}