001/* 002 * Units of Measurement API 003 * Copyright (c) 2014-2019, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-385 nor the names of its contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package javax.measure.spi; 031 032import java.lang.annotation.Annotation; 033import java.lang.reflect.InvocationTargetException; 034import java.lang.reflect.Method; 035import java.util.ArrayList; 036import java.util.Comparator; 037import java.util.List; 038import java.util.Objects; 039import java.util.Optional; 040import java.util.ServiceConfigurationError; 041import java.util.ServiceLoader; 042import java.util.concurrent.atomic.AtomicReference; 043import java.util.function.Predicate; 044import java.util.logging.Level; 045import java.util.logging.Logger; 046import java.util.stream.Collectors; 047import java.util.stream.Stream; 048import java.util.stream.StreamSupport; 049import javax.measure.Quantity; 050import javax.measure.format.QuantityFormat; 051import javax.measure.format.UnitFormat; 052 053/** 054 * Service Provider for Units of Measurement services. 055 * <p> 056 * All the methods in this class are safe to use by multiple concurrent threads. 057 * </p> 058 * 059 * @version 1.6, January 20, 2019 060 * @author Werner Keil 061 * @author Martin Desruisseaux 062 * @since 1.0 063 */ 064public abstract class ServiceProvider { 065 /** 066 * Class name of JSR-330 annotation for naming a service provider. 067 * We use reflection for keeping JSR-330 an optional dependency. 068 */ 069 private static final String NAMED_ANNOTATION = "javax.inject.Named"; 070 071 /** 072 * Class name of JSR-250 annotation for assigning a priority level to a service provider. 073 * We use reflection for keeping JSR-250 an optional dependency. 074 */ 075 private static final String PRIORITY_ANNOTATION = "javax.annotation.Priority"; 076 077 /** 078 * The current service provider, or {@code null} if not yet determined. 079 * 080 * <p>IMPLEMENTATION NOTE: We do not cache a list of all service providers because that list depends 081 * indirectly on the thread invoking the {@link #available()} method. More specifically, it depends 082 * on the context class loader. Furthermore caching the {@code ServiceProvider}s can be a source of 083 * memory leaks. See {@link ServiceLoader#load(Class)} API note for reference.</p> 084 */ 085 private static final AtomicReference<ServiceProvider> current = new AtomicReference<>(); 086 087 /** 088 * Creates a new service provider. Only to be used by subclasses. 089 */ 090 protected ServiceProvider() { 091 } 092 093 /** 094 * Allows to define a priority for a registered {@code ServiceProvider} instance. 095 * When multiple providers are registered in the system, the provider with the highest priority value is taken. 096 * 097 * <p>If the {@value #PRIORITY_ANNOTATION} annotation (from JSR-250) is present on the {@code ServiceProvider} 098 * implementation class, then that annotation is taken and this {@code getPriority()} method is ignored. 099 * Otherwise – if the {@code Priority} annotation is absent – this method is used as a fallback.</p> 100 * 101 * @return the provider's priority (default is 0). 102 */ 103 public int getPriority() { 104 return 0; 105 } 106 107 /** 108 * Returns the service to obtain a {@link SystemOfUnits}, or {@code null} if none. 109 * 110 * @return the service to obtain a {@link SystemOfUnits}, or {@code null}. 111 */ 112 public abstract SystemOfUnitsService getSystemOfUnitsService(); 113 114 /** 115 * Returns the service to obtain a {@link UnitFormat}, or {@code null} if none. 116 * 117 * @return the service to obtain a {@link UnitFormat}, or {@code null}. 118 * @deprecated Use {@link #getFormatService()}. This method will be removed in a future version, it is here for backward compatibility only. 119 */ 120 @Deprecated 121 public abstract UnitFormatService getUnitFormatService(); 122 123 /** 124 * Returns the service to obtain {@link UnitFormat} and {@link QuantityFormat} or {@code null} if none. 125 * 126 * @return the service to obtain a {@link UnitFormat} and {@link QuantityFormat}, or {@code null}. 127 * @since 2.0 128 */ 129 public abstract FormatService getFormatService(); 130 131 /** 132 * Returns a factory for the given {@link Quantity} type. 133 * 134 * @param <Q> 135 * the type of the {@link Quantity} instances created by the factory 136 * @param quantity 137 * the quantity type 138 * @return the {@link QuantityFactory} for the given type 139 */ 140 public abstract <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity); 141 142 /** 143 * A filter and a comparator for processing the stream of service providers. 144 * The two tasks (filtering and sorting) are implemented by the same class, 145 * but the filter task shall be used only if the name to search is non-null. 146 * The comparator is used in all cases, for sorting providers with higher priority first. 147 */ 148 private static final class Selector implements Predicate<ServiceProvider>, Comparator<ServiceProvider> { 149 /** 150 * The name of the provider to search, or {@code null} if no filtering by name is applied. 151 */ 152 private final String toSearch; 153 154 /** 155 * Class of the {@value #NAMED_ANNOTATION} and {@value #PRIORITY_ANNOTATION} annotations to search, 156 * or {@code null} if those classes are not on the classpath. 157 */ 158 private Class<? extends Annotation> nameAnnotation, priorityAnnotation; 159 160 /** 161 * The {@code value()} method in the {@code *Annotation} class, 162 * or {@code null} if those classes are not on the classpath. 163 */ 164 private Method nameGetter, priorityGetter; 165 166 /** 167 * Creates a new filter and comparator for a stream of service providers. 168 * 169 * @param name name of the desired service provider, or {@code null} if no filtering by name is applied. 170 */ 171 Selector(String name) { 172 toSearch = name; 173 try { 174 if (name != null) try { 175 nameAnnotation = Class.forName(NAMED_ANNOTATION).asSubclass(Annotation.class); 176 nameGetter = nameAnnotation.getMethod("value", (Class[]) null); 177 } catch (ClassNotFoundException e) { 178 // Ignore since JSR-330 is an optional dependency. 179 } 180 try { 181 priorityAnnotation = Class.forName(PRIORITY_ANNOTATION).asSubclass(Annotation.class); 182 priorityGetter = priorityAnnotation.getMethod("value", (Class[]) null); 183 } catch (ClassNotFoundException e) { 184 // Ignore since JSR-250 is an optional dependency. 185 } 186 } catch (NoSuchMethodException e) { 187 // Should never happen since value() is a standard public method of those annotations. 188 throw new ServiceConfigurationError("Can not get annotation value", e); 189 } 190 } 191 192 /** 193 * Returns {@code true} if the given service provider has the name we are looking for. 194 * This method shall be invoked only if a non-null name has been specified to the constructor. 195 * This method looks for the {@value #NAMED_ANNOTATION} annotation, and if none are found fallbacks on 196 * {@link ServiceProvider#toString()}. 197 */ 198 @Override 199 public boolean test(ServiceProvider provider) { 200 Object value = null; 201 if (nameGetter != null) { 202 Annotation a = provider.getClass().getAnnotation(nameAnnotation); 203 if (a != null) try { 204 value = nameGetter.invoke(a, (Object[]) null); 205 } catch (IllegalAccessException | InvocationTargetException e) { 206 // Should never happen since value() is a public method and should not throw exception. 207 throw new ServiceConfigurationError("Can not get annotation value", e); 208 } 209 } 210 if (value == null) { 211 value = provider.toString(); 212 } 213 return toSearch.equals(value); 214 } 215 216 /** 217 * Returns the priority of the given service provider. 218 * This method looks for the {@value #PRIORITY_ANNOTATION} annotation, 219 * and if none are found fallbacks on {@link ServiceProvider#getPriority()}. 220 */ 221 private int priority(ServiceProvider provider) { 222 if (priorityGetter != null) { 223 Annotation a = provider.getClass().getAnnotation(priorityAnnotation); 224 if (a != null) try { 225 return (Integer) priorityGetter.invoke(a, (Object[]) null); 226 } catch (IllegalAccessException | InvocationTargetException e) { 227 // Should never happen since value() is a public method and should not throw exception. 228 throw new ServiceConfigurationError("Can not get annotation value", e); 229 } 230 } 231 return provider.getPriority(); 232 } 233 234 /** 235 * Compares the given service providers for order based on their priority. 236 * The priority of each provider is determined as documented by {@link ServiceProvider#getPriority()}. 237 */ 238 @Override 239 public int compare(final ServiceProvider p1, final ServiceProvider p2) { 240 return Integer.compare(priority(p1), priority(p2)); 241 } 242 243 /** 244 * Gets all {@link ServiceProvider}s sorted by priority and optionally filtered by the name in this selector. 245 * The list of service providers is <strong>not</strong> cached because it depends on the context class loader, 246 * which itself depends on which thread is invoking this method. 247 */ 248 private Stream<ServiceProvider> stream() { 249 Stream<ServiceProvider> stream = StreamSupport.stream(ServiceLoader.load(ServiceProvider.class).spliterator(), false); 250 if (toSearch != null) { 251 stream = stream.filter(this); 252 } 253 return stream.sorted(this); 254 } 255 } 256 257 /** 258 * Returns the list of all service providers available for the current thread's context class loader. 259 * The {@linkplain #current() current} service provider is always the first item in the returned list. 260 * Other service providers after the first item may depend on the caller thread 261 * (see {@linkplain ServiceLoader#load(Class) service loader API note}). 262 * 263 * @return all service providers available for the current thread's context class loader. 264 */ 265 public static final List<ServiceProvider> available() { 266 ArrayList<ServiceProvider> providers = new Selector(null).stream().collect(Collectors.toCollection(ArrayList::new)); 267 /* 268 * Get the current service provider. If no provider has been set yet, set it now for 269 * consistency with the contract saying that the first item is the current provider. 270 */ 271 ServiceProvider first = current.get(); 272 if (first == null && !providers.isEmpty()) { 273 first = setDefault(providers.get(0)); 274 } 275 /* 276 * Make sure that 'first' is the first item in the 'providers' list. If that item appears 277 * somewhere else, we have to remove the second occurrence for avoiding duplicated elements. 278 * We compare the classes, not the instances, because new instances may be created each time 279 * this method is invoked and we have no guaranteed that implementors overrode 'equals'. 280 */ 281setcur: if (first != null) { 282 final Class<?> cf = first.getClass(); 283 final int size = providers.size(); 284 for (int i=0; i<size; i++) { 285 if (cf.equals(providers.get(i).getClass())) { 286 if (i == 0) break setcur; // No change needed (note: labeled breaks on if statements are legal). 287 providers.remove(i); 288 break; 289 } 290 } 291 providers.add(0, first); 292 } 293 return providers; 294 } 295 296 /** 297 * Returns the {@link ServiceProvider} with the specified name. 298 * The given name must match the name of at least one service provider available in the current thread's 299 * context class loader. The service provider names are the values of {@value #NAMED_ANNOTATION} annotations 300 * when present, or the value of {@link #toString()} method for providers without {@code Named} annotation. 301 * 302 * <p>Implementors are encouraged to provide an {@code Named} annotation or to override {@link #toString()} 303 * and use a unique enough name, e.g. the class name or other distinct attributes. 304 * Should multiple service providers nevertheless use the same name, the one with the highest 305 * {@linkplain #getPriority() priority} wins.</p> 306 * 307 * @param name 308 * the name of the service provider to return 309 * @return the {@link ServiceProvider} with the specified name 310 * @throws IllegalArgumentException 311 * if available service providers do not contain a provider with the specified name 312 * @throws NullPointerException 313 * if {@code name} is null 314 * @see #toString() 315 * @since 2.0 316 */ 317 public static ServiceProvider of(String name) { 318 Objects.requireNonNull(name); 319 Selector select = new Selector(name); 320 ServiceProvider p = current.get(); 321 if (p != null && select.test(p)) { 322 return p; 323 } 324 Optional<ServiceProvider> first = select.stream().findFirst(); 325 if (first.isPresent()) { 326 return first.get(); 327 } else { 328 throw new IllegalArgumentException("No measurement ServiceProvider " + name + " found ."); 329 } 330 } 331 332 /** 333 * Returns the current {@link ServiceProvider}. If necessary the {@link ServiceProvider} will be lazily loaded. 334 * <p> 335 * If there are no providers available, an {@linkplain IllegalStateException} is thrown. 336 * Otherwise the provider with the highest priority is used 337 * or the one explicitly designated via {@link #setCurrent(ServiceProvider)}. 338 * </p> 339 * 340 * @return the {@link ServiceProvider} used. 341 * @throws IllegalStateException 342 * if no {@link ServiceProvider} has been found. 343 * @see #getPriority() 344 * @see #setCurrent(ServiceProvider) 345 */ 346 public static final ServiceProvider current() { 347 ServiceProvider p = current.get(); 348 if (p == null) { 349 Optional<ServiceProvider> first = new Selector(null).stream().findFirst(); 350 if (first.isPresent()) { 351 p = first.get(); 352 } else { 353 throw new IllegalStateException("No measurement ServiceProvider found."); 354 } 355 p = setDefault(p); 356 } 357 return p; 358 } 359 360 /** 361 * Sets the given provider as the current one if and only if no other provider are currently set. 362 * If another provider is already set, that other provider is returned. 363 * 364 * @param provider the provider to set by default if no other provider is currently set. 365 * @return the current provider, which is the specified {@code provider} if no other provider 366 * was set before this method call. 367 */ 368 private static ServiceProvider setDefault(ServiceProvider provider) { 369 while (!current.compareAndSet(null, provider)) { 370 final ServiceProvider c = current.get(); 371 if (c != null) return c; 372 } 373 return provider; 374 } 375 376 /** 377 * Replaces the current {@link ServiceProvider}. 378 * 379 * @param provider 380 * the new {@link ServiceProvider} 381 * @return the replaced provider, or null. 382 */ 383 public static final ServiceProvider setCurrent(ServiceProvider provider) { 384 Objects.requireNonNull(provider); 385 ServiceProvider old = current.getAndSet(provider); 386 if (old != provider) { 387 Logger.getLogger("javax.measure.spi").log(Level.CONFIG, 388 "Measurement ServiceProvider {1,choice,0#set to|1#replaced by} {0}.", 389 new Object[] {provider.getClass().getName(), (old == null) ? 0 : 1}); 390 } 391 return old; 392 } 393}