001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2018, 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, Indriya nor the names of their 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 tech.units.indriya.spi;
031
032import javax.measure.Quantity;
033import javax.measure.spi.QuantityFactory;
034import javax.measure.spi.ServiceProvider;
035import javax.measure.spi.SystemOfUnitsService;
036import javax.measure.spi.UnitFormatService;
037
038import tech.units.indriya.quantity.DefaultQuantityFactory;
039
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.Comparator;
043import java.util.List;
044import java.util.Map;
045import java.util.ServiceLoader;
046import java.util.concurrent.ConcurrentHashMap;
047import java.util.logging.Level;
048import java.util.logging.Logger;
049
050/**
051 * This class extends the {@link javax.measure.spi.ServiceProvider} class and
052 * hereby uses the JDK {@link java.util.ServiceLoader} to load the required
053 * services.
054 *
055 * @author Werner Keil
056 * @version 1.0
057 * @since 1.0
058 */
059public class DefaultServiceProvider extends ServiceProvider implements Comparable<ServiceProvider> {
060        /**
061         * List of services loaded, per class.
062         */
063        @SuppressWarnings("rawtypes")
064        private final Map<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
065
066        private static final Comparator<Object> SERVICE_COMPARATOR = DefaultServiceProvider::compareServices;
067
068        @SuppressWarnings("rawtypes")
069        private final Map<Class, QuantityFactory> QUANTITY_FACTORIES = new ConcurrentHashMap<>();
070
071        /**
072         * Returns a priority value of 10.
073         *
074         * @return 10, overriding the default provider.
075         */
076        @Override
077        public int getPriority() {
078                return 10;
079        }
080
081        /**
082         * Loads and registers services.
083         *
084         * @param serviceType
085         *            The service type.
086         * @param <T>
087         *            the concrete type.
088         * @return the items found, never {@code null}.
089         */
090        protected <T> List<T> getServices(final Class<T> serviceType) {
091                @SuppressWarnings("unchecked")
092                List<T> found = (List<T>) servicesLoaded.get(serviceType);
093                if (found != null) {
094                        return found;
095                }
096
097                return loadServices(serviceType);
098        }
099
100        protected <T> T getService(Class<T> serviceType) {
101                List<T> services = getServices(serviceType);
102                if (services.isEmpty()) {
103                        return null;
104                }
105                return services.get(0);
106        }
107
108        static int compareServices(Object o1, Object o2) {
109                int prio1 = 0;
110                int prio2 = 0;
111                if (prio1 < prio2) {
112                        return 1;
113                }
114                if (prio2 < prio1) {
115                        return -1;
116                }
117                return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
118        }
119
120        /**
121         * Loads and registers services.
122         *
123         * @param serviceType
124         *            The service type.
125         * @param <T>
126         *            the concrete type.
127         * @return the items found, never {@code null}.
128         */
129        private <T> List<T> loadServices(final Class<T> serviceType) {
130                List<T> services = new ArrayList<>();
131                try {
132                        for (T t : ServiceLoader.load(serviceType)) {
133                                services.add(t);
134                        }
135                        Collections.sort(services, SERVICE_COMPARATOR);
136                        @SuppressWarnings("unchecked")
137                        final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
138                        return Collections.unmodifiableList(previousServices != null ? previousServices : services);
139                } catch (Exception e) {
140                        Logger.getLogger(DefaultServiceProvider.class.getName()).log(Level.WARNING,
141                                        "Error loading services of type " + serviceType, e);
142                        Collections.sort(services, SERVICE_COMPARATOR);
143                        return services;
144                }
145        }
146
147        @Override
148        public int compareTo(ServiceProvider o) {
149                return Integer.compare(getPriority(), o.getPriority());
150        }
151
152        @Override
153        public SystemOfUnitsService getSystemOfUnitsService() {
154                return getService(SystemOfUnitsService.class);
155        }
156
157        @Override
158        public UnitFormatService getUnitFormatService() {
159                return getService(UnitFormatService.class);
160        }
161
162         /**
163           * Return a factory for this quantity
164           * 
165           * @param quantity
166           *          the quantity type
167           * @return the {@link QuantityFactory}
168           * @throws NullPointerException
169           */
170          @Override
171          @SuppressWarnings("unchecked")
172          public final <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity) {
173            if (quantity == null)
174              throw new NullPointerException();
175            if (!QUANTITY_FACTORIES.containsKey(quantity)) {
176              synchronized (QUANTITY_FACTORIES) {
177                QUANTITY_FACTORIES.put(quantity, DefaultQuantityFactory.getInstance(quantity));
178              }
179            }
180            return QUANTITY_FACTORIES.get(quantity);
181          }
182}