package org.yajul.micro; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Scopes; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ClassFile; import javassist.bytecode.annotation.MemberValue; import javassist.bytecode.annotation.StringMemberValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yajul.micro.annotations.Component; import org.yajul.util.ReflectionUtil; import java.lang.annotation.Annotation; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Scans for component annotations. * <br>User: Josh * Date: Sep 7, 2008 * Time: 10:49:49 AM */ public class ComponentScanner extends AbstractAnnotationScanner { private static final Logger log = LoggerFactory.getLogger(ComponentScanner.class); private Set<Class<?>> implementations = new HashSet<Class<?>>(); private Map<Class, Class> keyToImpl = new HashMap<Class, Class>(); /** * Scan the files in the classloader that include the resource for component annotations * * @param resourceName the 'tag' resource that indicates which elements of the class loader to scan * @param classLoader the classloader */ public ComponentScanner(String resourceName, ClassLoader classLoader) { super(resourceName, classLoader); addAnnotation(Component.class); } public ComponentScanner(String resourceName) { super(resourceName, ReflectionUtil.getCurrentClassLoader()); addAnnotation(Component.class); } protected void handleAnnotation(String name, ClassFile classFile, Class<? extends Annotation> annotation) { if (annotation == Component.class) { // Register the component. String key = getAnnotationValue(classFile, annotation, "key"); if (log.isTraceEnabled()) log.trace("handleAnnotation() : name=" + name + " key=" + key); if (key == null) { Object implObject = MicroContainer.processName(name, getClassLoader()); if (implObject instanceof Class<?>) { Class<?> c = (Class<?>) implObject; implementations.add(c); } } else { String[] interfaceNames = classFile.getInterfaces(); Object implObject = MicroContainer.processName(name, getClassLoader()); if (implObject instanceof Class<?>) { Class<?> implClass = (Class<?>) implObject; Object keyObject = MicroContainer.processName(key, getClassLoader()); if (keyObject instanceof Class<?>) { Class<?> keyClass = (Class<?>) keyObject; keyToImpl.put(keyClass, implClass); } else { Class<?> keyClass = null; for (String interfaceName : interfaceNames) { if (log.isTraceEnabled()) log.trace("handleAnnotation() : key=" + key + " interfaceName=" + interfaceName); if (interfaceName.endsWith(key)) { try { keyClass = getClassLoader().loadClass(interfaceName); break; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } } if (keyClass != null) keyToImpl.put(keyClass, implClass); else { if (log.isTraceEnabled()) log.trace("handleAnnotation() : ignoring key " + key + ", it is not a class name or an interface name"); } } } else { if (log.isDebugEnabled()) log.debug("handleAnnotation() : ignoring name " + name + " it is not a class name."); } } } } /** * Get the value of the annotation on the Javassist {@link ClassFile}, or null * if the class doesn't have that annotation * * @param classFile the classFile * @param annotationType the annotation to look for * @param memberName the member to look for * @return the annotation value */ protected String getAnnotationValue(ClassFile classFile, Class<? extends Annotation> annotationType, String memberName) { AnnotationsAttribute visible = (AnnotationsAttribute) classFile.getAttribute(AnnotationsAttribute.visibleTag); if (visible != null) { javassist.bytecode.annotation.Annotation annotation = visible.getAnnotation(annotationType.getName()); if (annotation == null) { return null; } else { MemberValue memberValue = annotation.getMemberValue(memberName); if (memberValue instanceof StringMemberValue) { StringMemberValue stringMemberValue = (StringMemberValue) memberValue; return stringMemberValue.getValue(); } else { return null; } } } else { return null; } } public Injector createInjector() { scan(); ModuleList moduleList = new ModuleList(); moduleList.add(new AbstractModule() { protected void configure() { for (Class<?> implementation : implementations) { bind(implementation).in(Scopes.SINGLETON); } } }); moduleList.add(new AbstractModule() { protected void configure() { for (Map.Entry<Class, Class> entry : keyToImpl.entrySet()) { //noinspection unchecked bind(entry.getKey()).to(entry.getValue()).in(Scopes.SINGLETON); } } }); return moduleList.createInjector(); } }