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}