package co.codewizards.cloudstore.ls.client.handler;
import static co.codewizards.cloudstore.core.util.AssertUtil.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import co.codewizards.cloudstore.ls.core.dto.InverseServiceRequest;
@SuppressWarnings("rawtypes")
public class InverseServiceRequestHandlerManager {
private static final class HandlerClass implements Comparable<HandlerClass> {
public final Class<? extends InverseServiceRequestHandler> handlerClass;
public final int priority;
public HandlerClass(final Class<? extends InverseServiceRequestHandler> handlerClass, final int priority) {
this.handlerClass = assertNotNull(handlerClass, "handlerClass");
this.priority = priority;
}
/**
* {@inheritDoc}
* <p>
* <b>Important:</b> the implementation in {@code HandlerClass} sorts by priority first, then by name.
* The highest priority (greatest number) comes first!
*/
@Override
public int compareTo(HandlerClass o) {
int result = -1 * Integer.compare(this.priority, o.priority);
if (result != 0)
return result;
return this.handlerClass.getName().compareTo(o.handlerClass.getName());
}
}
private final Map<Class<?>, HandlerClass> requestType2HandlerClass = new HashMap<>();
private final Map<Class<?>, Class<? extends InverseServiceRequestHandler>> resolvedRequestType2HandlerClassCache = new HashMap<>();
private static final class Holder {
public static final InverseServiceRequestHandlerManager instance = new InverseServiceRequestHandlerManager();
}
public static InverseServiceRequestHandlerManager getInstance() {
return Holder.instance;
}
protected InverseServiceRequestHandlerManager() {
}
public InverseServiceRequestHandler getInverseServiceRequestHandler(Class<?> requestClass) {
assertNotNull(requestClass, "requestClass");
final Class<? extends InverseServiceRequestHandler> handlerClass = getInverseServiceRequestHandlerClass(requestClass);
if (handlerClass == null)
return null;
else
return newInstance(handlerClass);
}
private synchronized Class<? extends InverseServiceRequestHandler> getInverseServiceRequestHandlerClass(Class<?> requestClass) {
Class<? extends InverseServiceRequestHandler> result = resolvedRequestType2HandlerClassCache.get(requestClass);
if (result == null) {
final SortedSet<HandlerClass> handlerClasses = getInverseServiceRequestHandlerClasses(requestClass);
if (handlerClasses.isEmpty())
return null;
result = handlerClasses.iterator().next().handlerClass;
}
return result;
}
private synchronized SortedSet<HandlerClass> getInverseServiceRequestHandlerClasses(final Class<?> requestClass) {
assertNotNull(requestClass, "requestClass");
if (requestType2HandlerClass.isEmpty()) {
final Iterator<InverseServiceRequestHandler> iterator = ServiceLoader.load(InverseServiceRequestHandler.class).iterator();
while (iterator.hasNext()) {
final InverseServiceRequestHandler handler = iterator.next();
requestType2HandlerClass.put(handler.getInverseServiceRequestType(), new HandlerClass(handler.getClass(), handler.getPriority()));
}
}
final SortedSet<HandlerClass> handlerClasses = new TreeSet<>();
Class<?> c = requestClass;
while (c != null && c != Object.class) {
populateHandlerClasses(handlerClasses, c);
c = c.getSuperclass();
}
return handlerClasses;
}
private void populateHandlerClasses(final Set<HandlerClass> handlerClasses, final Class<?> requestClass) {
final HandlerClass handlerClass = requestType2HandlerClass.get(requestClass);
if (handlerClass != null)
handlerClasses.add(handlerClass);
for (final Class<?> iface : requestClass.getInterfaces())
populateHandlerClasses(handlerClasses, iface);
}
public InverseServiceRequestHandler getInverseServiceRequestHandlerOrFail(Class<?> requestClass) {
final InverseServiceRequestHandler handler = getInverseServiceRequestHandler(requestClass);
if (handler == null)
throw new IllegalArgumentException("Could not find a handler for this requestClass: " + requestClass.getName());
return handler;
}
public InverseServiceRequestHandler getInverseServiceRequestHandlerOrFail(final InverseServiceRequest request) {
assertNotNull(request, "request");
return getInverseServiceRequestHandlerOrFail(request.getClass());
}
private InverseServiceRequestHandler newInstance(Class<? extends InverseServiceRequestHandler> handlerClass) {
assertNotNull(handlerClass, "handlerClass");
try {
return handlerClass.newInstance();
} catch (final InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}