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}