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