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