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}