package com.netflix.governator.lifecycle; import java.beans.Introspector; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; import java.util.Iterator; import javax.annotation.Resource; import javax.annotation.Resources; import javax.naming.NamingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Injector; class ResourceMapper { private final Logger log = LoggerFactory.getLogger(getClass()); private Injector injector; private final Collection<ResourceLocator> resourceLocators; ResourceMapper(Injector injector, Collection<ResourceLocator> resourceLocators) { this.injector = injector; this.resourceLocators = resourceLocators; } public void map(Object obj, LifecycleMethods methods) throws Exception { if (methods.hasResources()) { for (Field field : methods.annotatedFields(Resources.class)) { Resources resources = field.getAnnotation(Resources.class); for (Resource resource : resources.value()) { setFieldResource(obj, field, resource); } } for (Field field : methods.annotatedFields(Resource.class)) { Resource resource = field.getAnnotation(Resource.class); setFieldResource(obj, field, resource); } for (Method method : methods.annotatedMethods(Resources.class)) { Resources resources = method.getAnnotation(Resources.class); for (Resource resource : resources.value()) { setMethodResource(obj, method, resource); } } for (Method method : methods.annotatedMethods(Resource.class)) { Resource resource = method.getAnnotation(Resource.class); setMethodResource(obj, method, resource); } for (Resources resources : methods.classAnnotations(Resources.class)) { for (Resource resource : resources.value()) { loadClassResource(resource); } } for (Resource resource : methods.classAnnotations(Resource.class)) { loadClassResource(resource); } } } private void loadClassResource(Resource resource) throws Exception { if ((resource.name().isEmpty()) || (resource.type() == Object.class)) { throw new Exception("Class resources must have both name() and type(): " + resource); } findResource(resource); } private void setMethodResource(Object obj, Method method, Resource resource) throws Exception { if ((method.getParameterTypes().length != 1) || (method.getReturnType() != Void.TYPE)) { throw new Exception(String.format("%s.%s() is not a proper JavaBean setter.", obj.getClass().getName(), method.getName())); } String beanName = method.getName(); if (beanName.toLowerCase().startsWith("set")) { beanName = beanName.substring("set".length()); } beanName = Introspector.decapitalize(beanName); String siteName = obj.getClass().getName() + "/" + beanName; resource = adjustResource(resource, method.getParameterTypes()[0], siteName); Object resourceObj = findResource(resource); method.setAccessible(true); method.invoke(obj, resourceObj); } private void setFieldResource(Object obj, Field field, Resource resource) throws Exception { String siteName = obj.getClass().getName() + "/" + field.getName(); Object resourceObj = findResource(adjustResource(resource, field.getType(), siteName)); field.setAccessible(true); field.set(obj, resourceObj); } private Resource adjustResource(final Resource resource, final Class<?> siteType, final String siteName) { return new Resource() { @Override public String name() { return (resource.name().length() == 0) ? siteName : resource.name(); } /** * Method needed for eventual java7 compatibility */ public String lookup() { return name(); } @Override public Class<?> type() { return (resource.type() == Object.class) ? siteType : resource.type(); } @Override public AuthenticationType authenticationType() { return resource.authenticationType(); } @Override public boolean shareable() { return resource.shareable(); } @Override public String mappedName() { return resource.mappedName(); } @Override public String description() { return resource.description(); } @Override public Class<? extends Annotation> annotationType() { return resource.annotationType(); } }; } private Object findResource(Resource resource) throws Exception { if (!resourceLocators.isEmpty()) { final Iterator<ResourceLocator> iterator = resourceLocators.iterator(); ResourceLocator locator = iterator.next(); ResourceLocator nextInChain = new ResourceLocator() { @Override public Object locate(Resource resource, ResourceLocator nextInChain) throws Exception { if (iterator.hasNext()) { return iterator.next().locate(resource, this); } return defaultFindResource(resource); } }; return locator.locate(resource, nextInChain); } return defaultFindResource(resource); } private Object defaultFindResource(Resource resource) throws Exception { if (injector == null) { throw new NamingException("Could not find resource: " + resource); } // noinspection unchecked log.debug("defaultFindResource using injector {}", System.identityHashCode(injector)); return injector.getInstance(resource.type()); } public Injector getInjector() { return injector; } public void setInjector(Injector injector) { this.injector = injector; } }