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