    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}