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}