001/*
002 * Copyright (c) 2012, 2016, 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.spi;
017
018import javax.annotation.Priority;
019import javax.money.spi.ServiceProvider;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.Comparator;
023import java.util.List;
024import java.util.ServiceLoader;
025import java.util.concurrent.ConcurrentHashMap;
026import java.util.logging.Level;
027import java.util.logging.Logger;
028
029/**
030 * This class implements the (default) {@link javax.money.spi.ServiceProvider} interface and hereby uses the JDK
031 * {@link java.util.ServiceLoader} to load the services required.
032 *
033 * @author Anatole Tresch
034 * @author Werner Keil
035 */
036public class PriorityAwareServiceProvider implements ServiceProvider {
037    /**
038     * List of services loaded, per class.
039     */
040    private final ConcurrentHashMap<Class, List<Object>> servicesLoaded = new ConcurrentHashMap<>();
041
042    private static final Comparator<Object> SERVICE_COMPARATOR = new Comparator<Object>(){
043
044        @Override
045        public int compare(Object o1, Object o2) {
046            return PriorityAwareServiceProvider.compareServices(o1, o2);
047        }
048    };
049
050    /**
051     * Returns a priority value of 10.
052     *
053     * @return 10, overriding the default provider.
054     */
055    @Override
056    public int getPriority() {
057        return 10;
058    }
059
060    /**
061     * Loads and registers services.
062     *
063     * @param serviceType The service type.
064     * @param <T>         the concrete type.
065     * @return the items found, never {@code null}.
066     */
067    @Override
068    public <T> List<T> getServices(final Class<T> serviceType) {
069        @SuppressWarnings("unchecked")
070        List<T> found = (List<T>) servicesLoaded.get(serviceType);
071        if (found != null) {
072            return found;
073        }
074
075        return loadServices(serviceType);
076    }
077
078    @Override
079    public <T> T getService(Class<T> serviceType) {
080        List<T> services = getServices(serviceType);
081        if(services.isEmpty()){
082            return null;
083        }
084        return services.get(0);
085    }
086
087    public static int compareServices(Object o1, Object o2) {
088        int prio1 = 0;
089        int prio2 = 0;
090        Priority prio1Annot = o1.getClass().getAnnotation(Priority.class);
091        if (prio1Annot != null) {
092            prio1 = prio1Annot.value();
093        }
094        Priority prio2Annot = o2.getClass().getAnnotation(Priority.class);
095        if (prio2Annot != null) {
096            prio2 = prio2Annot.value();
097        }
098        if (prio1 < prio2) {
099            return 1;
100        }
101        if (prio2 < prio1) {
102            return -1;
103        }
104        return o2.getClass().getSimpleName().compareTo(o1.getClass().getSimpleName());
105    }
106
107    /**
108     * Loads and registers services.
109     *
110     * @param serviceType The service type.
111     * @param <T>         the concrete type.
112     * @return the items found, never {@code null}.
113     */
114    private <T> List<T> loadServices(final Class<T> serviceType) {
115        List<T> services = new ArrayList<>();
116        try {
117            for (T t : ServiceLoader.load(serviceType)) {
118                services.add(t);
119            }
120            Collections.sort(services, SERVICE_COMPARATOR);
121            @SuppressWarnings("unchecked")
122            final List<T> previousServices = (List<T>) servicesLoaded.putIfAbsent(serviceType, (List<Object>) services);
123            return Collections.unmodifiableList(previousServices != null ? previousServices : services);
124        } catch (Exception e) {
125            Logger.getLogger(PriorityAwareServiceProvider.class.getName()).log(Level.WARNING,
126                    "Error loading services of type " + serviceType, e);
127            Collections.sort(services, SERVICE_COMPARATOR);
128            return services;
129        }
130    }
131
132}