package org.deephacks.westty.config; import com.google.common.base.Optional; import org.deephacks.confit.ConfigContext; import org.deephacks.confit.model.AbortRuntimeException; import org.deephacks.confit.model.Events; import org.deephacks.westty.server.ServerName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.enterprise.inject.Produces; import javax.enterprise.inject.spi.InjectionPoint; import javax.inject.Inject; import java.lang.reflect.Constructor; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; /** * This class enables injection of configurable classes that have an @Id * same as the running server instance. This also means that the configurable * class must have a constructor that takes its id as an argument. * * @param <T> configurable class */ public class ServerSpecificConfigProxy<T> { private T config; private ServerSpecificConfigProxy(T config){ this.config = config; } /** * @return The configurable server specific instance or an empty default * instance if it does not exist in the configuration bean manager. */ public T get(){ return config; } static class ServerSpecificConfigProxyProducer<T> { private static final Logger log = LoggerFactory.getLogger(ServerSpecificConfigProxyProducer.class); @Inject private ServerName serverName; @Inject private ConfigContext config; @Produces public ServerSpecificConfigProxy<T> produceServerConfigProxy(InjectionPoint ip){ Class<?> declaringClass = ip.getMember().getDeclaringClass(); Class<?> configCls = getParameterizedType(declaringClass, ip.getAnnotated().getBaseType()); try { Optional<?> optionalServer = config.get(serverName.getName(), configCls); if (optionalServer.isPresent()) { return new ServerSpecificConfigProxy(optionalServer.get()); } else { log.debug("Config instance {} of type {} not found", serverName.getName(), configCls); Object config = newInstance(configCls, serverName.getName()); return new ServerSpecificConfigProxy(config); } } catch (AbortRuntimeException e) { if (e.getEvent().getCode() != Events.CFG304){ throw e; } log.debug("Config instance {} of type {} not found", serverName.getName(), configCls); Object config = newInstance(configCls, serverName.getName()); return new ServerSpecificConfigProxy(config); } } Class<?> getParameterizedType(Class<?> cls, final Type type) { if (!ParameterizedType.class.isAssignableFrom(type.getClass())) { throw new IllegalArgumentException("ServerSpecificConfigProxy does not have generic type " + type); } ParameterizedType ptype = (ParameterizedType) type; Type[] targs = ptype.getActualTypeArguments(); for (Type aType : targs) { return extractClass(cls, aType); } throw new IllegalArgumentException("Could not get generic type from ServerSpecificConfigProxy " + type); } private static Class<?> extractClass(Class<?> ownerClass, Type arg) { if (arg instanceof ParameterizedType) { return extractClass(ownerClass, ((ParameterizedType) arg).getRawType()); } else if (arg instanceof GenericArrayType) { throw new UnsupportedOperationException("GenericArray types are not supported."); } else if (arg instanceof TypeVariable) { throw new UnsupportedOperationException("GenericArray types are not supported."); } return (arg instanceof Class ? (Class<?>) arg : Object.class); } Object newInstance(Class<?> type, String name) { try { Class<?> enclosing = type.getEnclosingClass(); if (enclosing == null) { Constructor<?> c = type.getDeclaredConstructor(String.class); c.setAccessible(true); return type.cast(c.newInstance(name)); } Object o = enclosing.newInstance(); Constructor<?> cc = type.getDeclaredConstructor(enclosing, String.class); cc.setAccessible(true); return type.cast(cc.newInstance(o, name)); } catch (Exception e) { throw new RuntimeException(e); } } static Class<?> forName(String className) { try { return Thread.currentThread().getContextClassLoader().loadClass(className); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } } }