001/**
002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta.internal;
017
018import javax.annotation.Priority;
019import javax.money.spi.ServiceProvider;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import java.util.ServiceLoader;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.logging.Level;
026import java.util.logging.Logger;
027
028/**
029 * This class implements the (default) {@link javax.money.spi.ServiceProvider} interface and hereby uses the JDK
030 * {@link java.util.ServiceLoader} to load the services required.
031 *
032 * @author Anatole Tresch
033 */
034public class PriorityAwareServiceProvider implements ServiceProvider {
035    /**
036     * List of services loaded, per class.
037     */
038    private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
039
040    /**
041     * Returns a priority value of 10.
042     *
043     * @return 10, overriding the default provider.
044     */
045    @Override
046    public int getPriority() {
047        return 10;
048    }
049
050    /**
051     * Loads and registers services.
052     *
053     * @param serviceType The service type.
054     * @param <T>         the concrete type.
055     * @return the items found, never {@code null}.
056     */
057    @Override
058    public <T> List<T> getServices(final Class<T> serviceType) {
059        @SuppressWarnings("unchecked")
060        List<T> found = (List<T>) servicesLoaded.get(serviceType);
061        if (found != null) {
062            return found;
063        }
064
065        return loadServices(serviceType);
066    }
067
068    public static int compareServices(Object o1, Object o2) {
069        int prio1 = 0;
070        int prio2 = 0;
071        Priority prio1Annot = o1.getClass().getAnnotation(Priority.class);
072        if (prio1Annot != null) {
073            prio1 = prio1Annot.value();
074        }
075        Priority prio2Annot = o2.getClass().getAnnotation(Priority.class);
076        if (prio2Annot != null) {
077            prio2 = prio2Annot.value();
078        }
079        if (prio1 < prio2) {
080            return 1;
081        }
082        if (prio2 < prio1) {
083            return -1;
084        }
085        return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
086    }
087
088    /**
089     * Loads and registers services.
090     *
091     * @param serviceType The service type.
092     * @param <T>         the concrete type.
093     * @return the items found, never {@code null}.
094     */
095    private <T> List<T> loadServices(final Class<T> serviceType) {
096        List<T> services = new ArrayList<>();
097        try {
098            for (T t : ServiceLoader.load(serviceType)) {
099                services.add(t);
100            }
101            services.sort(PriorityAwareServiceProvider::compareServices);
102            @SuppressWarnings("unchecked")
103            final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
104            return Collections.unmodifiableList(previousServices != null ? previousServices : services);
105        } catch (Exception e) {
106            Logger.getLogger(PriorityAwareServiceProvider.class.getName()).log(Level.WARNING,
107                    "Error loading services of type " + serviceType, e);
108            services.sort(PriorityAwareServiceProvider::compareServices);
109            return services;
110        }
111    }
112
113}