001/*
002 * CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU
003 * ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT. PLEASE READ THE TERMS AND CONDITIONS OF THIS
004 * AGREEMENT CAREFULLY. BY DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF
005 * THE AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE" BUTTON AT THE
006 * BOTTOM OF THIS PAGE. Specification: JSR-354 Money and Currency API ("Specification") Copyright
007 * (c) 2012-2013, Credit Suisse All rights reserved.
008 */
009package org.javamoney.moneta.internal;
010
011import javax.annotation.Priority;
012import javax.money.spi.ServiceProvider;
013import java.util.ArrayList;
014import java.util.Collections;
015import java.util.List;
016import java.util.ServiceLoader;
017import java.util.concurrent.ConcurrentHashMap;
018import java.util.logging.Level;
019import java.util.logging.Logger;
020
021/**
022 * This class implements the (default) {@link javax.money.spi.ServiceProvider} interface and hereby uses the JDK
023 * {@link java.util.ServiceLoader} to load the services required.
024 *
025 * @author Anatole Tresch
026 */
027public class PriorityAwareServiceProvider implements ServiceProvider {
028    /**
029     * List of services loaded, per class.
030     */
031    private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
032
033    /**
034     * Returns a prioritx value of 10.
035     *
036     * @return 10, overriding the default provider.
037     */
038    @Override
039    public int getPriority() {
040        return 10;
041    }
042
043    /**
044     * Loads and registers services.
045     *
046     * @param serviceType The service type.
047     * @param <T>         the concrete type.
048     * @param defaultList the list of items returned, if no services were found.
049     * @return the items found, never {@code null}.
050     */
051    @Override
052    public <T> List<T> getServices(final Class<T> serviceType) {
053        @SuppressWarnings("unchecked")
054        List<T> found = (List<T>) servicesLoaded.get(serviceType);
055        if (found != null) {
056            return found;
057        }
058
059        return loadServices(serviceType);
060    }
061
062    public static int compareServices(Object o1, Object o2) {
063        int prio1 = 0;
064        int prio2 = 0;
065        Priority prio1Annot = o1.getClass().getAnnotation(Priority.class);
066        if (prio1Annot != null) {
067            prio1 = prio1Annot.value();
068        }
069        Priority prio2Annot = o2.getClass().getAnnotation(Priority.class);
070        if (prio2Annot != null) {
071            prio2 = prio2Annot.value();
072        }
073        if (prio1 < prio2) {
074            return 1;
075        }
076        if (prio2 < prio1) {
077            return -1;
078        }
079        return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
080    }
081
082    /**
083     * Loads and registers services.
084     *
085     * @param serviceType The service type.
086     * @param <T>         the concrete type.
087     * @param defaultList the list of items returned, if no services were found.
088     * @return the items found, never {@code null}.
089     */
090    private <T> List<T> loadServices(final Class<T> serviceType) {
091        List<T> services = new ArrayList<>();
092        try {
093            for (T t : ServiceLoader.load(serviceType)) {
094                services.add(t);
095            }
096            services.sort(PriorityAwareServiceProvider::compareServices);
097            @SuppressWarnings("unchecked")
098            final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
099            return Collections.unmodifiableList(previousServices != null ? previousServices : services);
100        } catch (Exception e) {
101            Logger.getLogger(PriorityAwareServiceProvider.class.getName()).log(Level.WARNING,
102                    "Error loading services of type " + serviceType, e);
103            services.sort(PriorityAwareServiceProvider::compareServices);
104            return services;
105        }
106    }
107
108}