package org.deephacks.confit.spi; import com.google.common.base.Objects; import com.google.common.base.Optional; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; /** * Lookup is responsible for solving the problem of dynamic service discovery in different * environments like standard Java SE ServiceLoader, CDI, Spring, OSGi etc. * * Service providers register themselves and clients query for a suitable provider, * without knowing how lookup is performed. The purpose is to achieve modularity and * separation between components. * */ public class Lookup extends LookupProvider { private ArrayList<LookupProvider> lookupProviders = new ArrayList<>(); private static Lookup LOOKUP; private static PropertyManager propertyManager; static { propertyManager = Lookup.get().lookup(PropertyManager.class); } protected Lookup() { } /** * Acquire the Lookup registry. * * @return The lookup registry. */ public static Lookup get() { if (LOOKUP != null) { return LOOKUP; } synchronized (Lookup.class) { // allow for override of the Lookup.class String overrideClassName = System.getProperty(Lookup.class.getName()); ClassLoader l = Thread.currentThread().getContextClassLoader(); try { if (overrideClassName != null && !"".equals(overrideClassName)) { LOOKUP = (Lookup) Class.forName(overrideClassName, true, l).newInstance(); } else { LOOKUP = new Lookup(); } } catch (Exception e) { // ignore } // ServiceLoader and CDI are used by defaults, important that // CDI is used first so that beans are enabled for injection CdiLookup cdiLookup = new CdiLookup(); LOOKUP.lookupProviders.add(cdiLookup); ServiceLoaderLookup serviceLoaderLookup = new ServiceLoaderLookup(); LOOKUP.lookupProviders.add(serviceLoaderLookup); // Use ServiceLoader to find other LookupProviders Collection<LookupProvider> providers = serviceLoaderLookup .lookupAll(LookupProvider.class); LOOKUP.lookupProviders.addAll(providers); } return LOOKUP; } @Override public <T> Collection<T> lookupAll(Class<T> clazz) { ArrayList<T> result = new ArrayList<>(); for (LookupProvider lp : lookupProviders) { result.addAll(lp.lookupAll(clazz)); } return result; } @SuppressWarnings("unchecked") public <T> T lookup(Class<T> clazz) { Object object = objectRegistry.get(clazz); if (object != null) { return (T) object; } for (LookupProvider lp : lookupProviders) { Collection<T> result = lp.lookupAll(clazz); if (result.isEmpty()) { continue; } T prefered = lookupPrefered(clazz, result); return prefered != null ? prefered : result.iterator().next(); } return lookupPrefered(clazz, new ArrayList<T>()); } @SuppressWarnings("unchecked") public <T> T lookup(Class<T> clazz, Class<? extends T> defaultClass) { T instance = lookup(clazz); if (instance != null) { return instance; } try { return defaultClass.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } public <T> void register(Class<T> clazz, T object) { objectRegistry.put(clazz, object); } public void registerLookup(LookupProvider provider) { lookupProviders.add(provider); } public void unregisterLookup(LookupProvider provider) { lookupProviders.remove(provider); } public String toString() { return Objects.toStringHelper(Lookup.class).add("LOOKUP", LOOKUP.getClass().getName()) .add("lookupProviders", lookupProviders).toString(); } <T> T lookupPrefered(Class<T> clazz, Collection<T> instances) { final Optional<String> preferred; if (propertyManager != null) { preferred = propertyManager.get(clazz.getName()); } else { preferred = Optional.absent(); } return getPreferredInstance(instances, preferred); } static <T> T getPreferredInstance(Collection<T> instances, Optional<String> preferred) { LinkedList<T> preferredInstances = new LinkedList<>(); if (instances == null || instances.size() == 0) { return null; } for (T instance : instances) { if (preferred.isPresent() && instance.getClass().getName().equals(preferred.get())) { return instance; } else { if (instance.getClass().getName().toLowerCase().contains("default")) { preferredInstances.addLast(instance); } else { preferredInstances.addFirst(instance); } } } if (preferredInstances.isEmpty()) { throw new IllegalArgumentException("Could not find preferred instance [" + preferred.get() + "] " + "among available instances [" + instances + "]."); } return preferredInstances.peekFirst(); } static Object newInstance(String className) { try { Class<?> type = forName(className); Class<?> enclosing = type.getEnclosingClass(); if (enclosing == null) { Constructor<?> c = type.getDeclaredConstructor(); c.setAccessible(true); return c.newInstance(); } Object o = enclosing.newInstance(); Constructor<?> cc = type.getDeclaredConstructor(enclosing); cc.setAccessible(true); return cc.newInstance(o); } catch (Exception e) { throw new RuntimeException(e); } } private static final Map<String, Class<?>> ALL_PRIMITIVE_TYPES = new HashMap<>(); private static final Map<String, Class<?>> ALL_PRIMITIVE_NUMBERS = new HashMap<>(); static { for (Class<?> primitiveNumber : Arrays.asList(byte.class, short.class, int.class, long.class, float.class, double.class)) { ALL_PRIMITIVE_NUMBERS.put(primitiveNumber.getName(), primitiveNumber); ALL_PRIMITIVE_TYPES.put(primitiveNumber.getName(), primitiveNumber); } for (Class<?> primitive : Arrays.asList(char.class, boolean.class)) { ALL_PRIMITIVE_TYPES.put(primitive.getName(), primitive); } } static Class<?> forName(String className) { try { Class<?> primitive = ALL_PRIMITIVE_TYPES.get(className); return primitive != null ? primitive : Thread.currentThread() .getContextClassLoader().loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } }