001package co.codewizards.cloudstore.ls.client.handler;
002
003import static java.util.Objects.*;
004
005import java.util.HashMap;
006import java.util.Iterator;
007import java.util.Map;
008import java.util.ServiceLoader;
009import java.util.Set;
010import java.util.SortedSet;
011import java.util.TreeSet;
012
013import co.codewizards.cloudstore.ls.core.dto.InverseServiceRequest;
014
015@SuppressWarnings("rawtypes")
016public class InverseServiceRequestHandlerManager {
017
018        private static final class HandlerClass implements Comparable<HandlerClass> {
019                public final Class<? extends InverseServiceRequestHandler> handlerClass;
020                public final int priority;
021
022                public HandlerClass(final Class<? extends InverseServiceRequestHandler> handlerClass, final int priority) {
023                        this.handlerClass = requireNonNull(handlerClass, "handlerClass");
024                        this.priority = priority;
025                }
026
027                /**
028                 * {@inheritDoc}
029                 * <p>
030                 * <b>Important:</b> the implementation in {@code HandlerClass} sorts by priority first, then by name.
031                 * The highest priority (greatest number) comes first!
032                 */
033                @Override
034                public int compareTo(HandlerClass o) {
035                        int result = -1 * Integer.compare(this.priority, o.priority);
036                        if (result != 0)
037                                return result;
038
039                        return this.handlerClass.getName().compareTo(o.handlerClass.getName());
040                }
041        }
042
043        private final Map<Class<?>, HandlerClass> requestType2HandlerClass = new HashMap<>();
044        private final Map<Class<?>, Class<? extends InverseServiceRequestHandler>> resolvedRequestType2HandlerClassCache = new HashMap<>();
045
046        private static final class Holder {
047                public static final InverseServiceRequestHandlerManager instance = new InverseServiceRequestHandlerManager();
048        }
049
050        public static InverseServiceRequestHandlerManager getInstance() {
051                return Holder.instance;
052        }
053
054        protected InverseServiceRequestHandlerManager() {
055        }
056
057        public InverseServiceRequestHandler getInverseServiceRequestHandler(Class<?> requestClass) {
058                requireNonNull(requestClass, "requestClass");
059
060                final Class<? extends InverseServiceRequestHandler> handlerClass = getInverseServiceRequestHandlerClass(requestClass);
061                if (handlerClass == null)
062                        return null;
063                else
064                        return newInstance(handlerClass);
065        }
066
067        private synchronized Class<? extends InverseServiceRequestHandler> getInverseServiceRequestHandlerClass(Class<?> requestClass) {
068                Class<? extends InverseServiceRequestHandler> result = resolvedRequestType2HandlerClassCache.get(requestClass);
069                if (result == null) {
070                        final SortedSet<HandlerClass> handlerClasses = getInverseServiceRequestHandlerClasses(requestClass);
071                        if (handlerClasses.isEmpty())
072                                return null;
073
074                        result = handlerClasses.iterator().next().handlerClass;
075                }
076                return result;
077        }
078
079        private synchronized SortedSet<HandlerClass> getInverseServiceRequestHandlerClasses(final Class<?> requestClass) {
080                requireNonNull(requestClass, "requestClass");
081
082                if (requestType2HandlerClass.isEmpty()) {
083                        final Iterator<InverseServiceRequestHandler> iterator = ServiceLoader.load(InverseServiceRequestHandler.class).iterator();
084                        while (iterator.hasNext()) {
085                                final InverseServiceRequestHandler handler = iterator.next();
086                                requestType2HandlerClass.put(handler.getInverseServiceRequestType(), new HandlerClass(handler.getClass(), handler.getPriority()));
087                        }
088                }
089
090                final SortedSet<HandlerClass> handlerClasses = new TreeSet<>();
091                Class<?> c = requestClass;
092                while (c != null && c != Object.class) {
093                        populateHandlerClasses(handlerClasses, c);
094                        c = c.getSuperclass();
095                }
096                return handlerClasses;
097        }
098
099        private void populateHandlerClasses(final Set<HandlerClass> handlerClasses, final Class<?> requestClass) {
100                final HandlerClass handlerClass = requestType2HandlerClass.get(requestClass);
101                if (handlerClass != null)
102                        handlerClasses.add(handlerClass);
103
104                for (final Class<?> iface : requestClass.getInterfaces())
105                        populateHandlerClasses(handlerClasses, iface);
106        }
107
108        public InverseServiceRequestHandler getInverseServiceRequestHandlerOrFail(Class<?> requestClass) {
109                final InverseServiceRequestHandler handler = getInverseServiceRequestHandler(requestClass);
110                if (handler == null)
111                        throw new IllegalArgumentException("Could not find a handler for this requestClass: " + requestClass.getName());
112
113                return handler;
114        }
115
116        public InverseServiceRequestHandler getInverseServiceRequestHandlerOrFail(final InverseServiceRequest request) {
117                requireNonNull(request, "request");
118                return getInverseServiceRequestHandlerOrFail(request.getClass());
119        }
120
121        private InverseServiceRequestHandler newInstance(Class<? extends InverseServiceRequestHandler> handlerClass) {
122                requireNonNull(handlerClass, "handlerClass");
123                try {
124                        return handlerClass.newInstance();
125                } catch (final InstantiationException | IllegalAccessException e) {
126                        throw new RuntimeException(e);
127                }
128        }
129}