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