001/* 002 * Units of Measurement API 003 * Copyright (c) 2014-2016, Jean-Marie Dautelle, Werner Keil, V2COM. 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-363 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.util.ArrayList; 033import java.util.Arrays; 034import java.util.Comparator; 035import java.util.List; 036import java.util.ServiceLoader; 037import java.util.logging.Level; 038import java.util.logging.Logger; 039import javax.measure.Quantity; 040import javax.measure.format.UnitFormat; 041 042/** 043 * Service Provider for Units of Measurement services. 044 * <p> 045 * All the methods in this class are safe for use by multiple concurrent threads. 046 * 047 * @version 0.9.3, July 2, 2016 048 * @author Werner Keil 049 * @author Martin Desruisseaux 050 */ 051public abstract class ServiceProvider { 052 /** 053 * Synchronization lock for searching or setting the service providers. 054 */ 055 private static final Object LOCK = new Object(); 056 057 /** 058 * All service providers found, sorted in preference order. Array content shall never been changed after initialization; if we want to change the 059 * array content, a new array shall be created. 060 */ 061 private static volatile ServiceProvider[] providers; 062 063 /** 064 * Creates a new service provider. Only to be used by subclasses. 065 */ 066 protected ServiceProvider() { 067 } 068 069 /** 070 * This method allows to define a priority for a registered ServiceProvider instance. When multiple providers are registered in the system the 071 * provider with the highest priority value is taken. 072 * 073 * @return the provider's priority (default is 0). 074 */ 075 public int getPriority() { 076 return 0; 077 } 078 079 /** 080 * Returns the service to obtain a {@link SystemOfUnits}, or {@code null} if none. 081 * 082 * @return the service to obtain a {@link SystemOfUnits}, or {@code null}. 083 */ 084 public abstract SystemOfUnitsService getSystemOfUnitsService(); 085 086 /** 087 * Returns the service to obtain a {@link UnitFormat}, or {@code null} if none. 088 * 089 * @return the service to obtain a {@link UnitFormat}, or {@code null}. 090 */ 091 public abstract UnitFormatService getUnitFormatService(); 092 093 /** 094 * Return a factory for this {@link Quantity}. 095 * 096 * @param quantity 097 * the quantity 098 * @return the {@link QuantityFactory} 099 */ 100 public abstract <Q extends Quantity<Q>> QuantityFactory<Q> getQuantityFactory(Class<Q> quantity); 101 102 /** 103 * Gets all {@link ServiceProvider}. This method loads the provider when first needed. 104 */ 105 private static ServiceProvider[] getProviders() { 106 ServiceProvider[] p = providers; 107 if (p == null) { 108 synchronized (LOCK) { 109 p = providers; 110 if (p == null) { 111 final List<ServiceProvider> loaded = new ArrayList<ServiceProvider>(); 112 for (ServiceProvider provider : ServiceLoader.load(ServiceProvider.class)) { 113 loaded.add(provider); 114 } 115 p = loaded.toArray(new ServiceProvider[loaded.size()]); 116 Arrays.sort(p, new Comparator<ServiceProvider>() { 117 @Override 118 public int compare(ServiceProvider p1, ServiceProvider p2) { 119 return p2.getPriority() - p1.getPriority(); 120 } 121 }); 122 providers = p; // Set only on success. 123 } 124 } 125 } 126 return p; 127 } 128 129 /** 130 * Returns the list of available service providers. 131 * 132 * @return all available service providers. 133 */ 134 public static List<ServiceProvider> available() { 135 return Arrays.asList(getProviders()); // .clone(); 136 } 137 138 /** 139 * Returns the current {@link ServiceProvider}. If necessary the {@link ServiceProvider} will be lazily loaded. 140 * 141 * @return the {@link ServiceProvider} used. 142 * @throws IllegalStateException 143 * if no {@link ServiceProvider} has been found. 144 */ 145 public static ServiceProvider current() { 146 ServiceProvider[] p = getProviders(); 147 if (p.length != 0) { 148 return p[0]; 149 } 150 throw new IllegalStateException("No measurement ServiceProvider found."); 151 } 152 153 /** 154 * Replaces the current {@link ServiceProvider}. 155 * 156 * @param provider 157 * the new {@link ServiceProvider} 158 * @return the removed provider, or null. 159 */ 160 public static ServiceProvider setCurrent(ServiceProvider provider) { 161 if (provider == null) { 162 throw new NullPointerException(); 163 } 164 synchronized (LOCK) { 165 ServiceProvider[] p = getProviders(); 166 ServiceProvider old = (p.length != 0) ? p[0] : null; 167 if (provider != old) { 168 List<ServiceProvider> copy = new ArrayList<ServiceProvider>(Arrays.asList(p)); 169 copy.remove(provider); 170 copy.add(0, provider); 171 providers = copy.toArray(new ServiceProvider[copy.size()]); 172 173 // Keep the log inside the synchronized block for making sure that the order 174 // or logging messages matches the order in which ServiceProviders were set. 175 Logger.getLogger("javax.measure.spi").log(Level.CONFIG, 176 (old == null) ? "Measurement ServiceProvider set to {0}" : "Measurement ServiceProvider replaced by {0}", provider.getClass().getName()); 177 } 178 return old; 179 } 180 } 181}