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}