/* * JBoss, Home of Professional Open Source * Copyright 2007, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.deployment; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.jboss.deployers.structure.spi.DeploymentUnit; import org.jboss.deployers.vfs.spi.structure.VFSDeploymentUnit; import org.jboss.logging.Logger; import org.jboss.virtual.VirtualFile; import org.jboss.virtual.VirtualFileFilter; import org.jboss.virtual.VirtualFileVisitor; import org.jboss.virtual.VisitorAttributes; /** * A VirtualFileVisitor that traverses unit root and determines the * class files that are annotated. * * @author Scott.Stark@jboss.org * @version $Revision: 85945 $ */ public class AnnotatedClassFilter implements VirtualFileVisitor { private static Logger log = Logger.getLogger(AnnotatedClassFilter.class); private ClassLoader loader; private int rootLength; private HashSet<String> childPaths = new HashSet<String>(); private HashMap<VirtualFile, Class<?>> pathToClasses = new HashMap<VirtualFile, Class<?>>(); private String clientClassName; public AnnotatedClassFilter(VFSDeploymentUnit unit, ClassLoader loader, VirtualFile classpathRoot) { this(unit, loader, classpathRoot, ""); } public AnnotatedClassFilter(VFSDeploymentUnit unit, ClassLoader loader, VirtualFile classpathRoot, String clientClassName) { this.loader = loader; this.clientClassName = clientClassName; // Work out the root length. If there is a root, we need to add one to jump across the next / String rootName = classpathRoot.getPathName(); rootLength = rootName.length(); if (rootLength > 0) rootLength += 1; List<DeploymentUnit> children = unit.getChildren(); if(children != null) { for(DeploymentUnit cu : children) { String path = cu.getName(); childPaths.add(path); } } } public Map<VirtualFile, Class<?>> getAnnotatedClasses() { return pathToClasses; } public VisitorAttributes getAttributes() { VisitorAttributes attributes = new VisitorAttributes(); attributes.setIncludeRoot(true); attributes.setRecurseFilter(new NoChildFilter()); return attributes; } public void visit(VirtualFile file) { try { if(file.isLeaf()) { accepts(file); } } catch (IOException e) { throw new Error("Error visiting " + file, e); } } public boolean accepts(VirtualFile file) { boolean accepts = file.getPathName().endsWith(".class"); if(accepts) { accepts = false; String className = null; try { className = getClassName(file); Class<?> c = loader.loadClass(className); boolean hasAnnotations = hasAnnotations(c); boolean includeClass = false; if(clientClassName != null) { includeClass = className.equals(clientClassName) && hasAnnotations; } else { includeClass = hasAnnotations; } if(includeClass) { pathToClasses.put(file, c); accepts = true; } } catch(NoClassDefFoundError ignored) { log.debug("Incomplete class: "+className+", NCDFE: "+ignored); } catch(Exception ignored) { if(log.isTraceEnabled()) log.trace("Failed to load class: "+className, ignored); } } return accepts; } protected String getFilePath(VirtualFile file) { String path = null; try { path = file.toURI().toString(); } catch(Exception e) { } return path; } /** * Search the classpaths for the root of this file. * * @param classFile the class file * @return fqn class name * @throws IOException for any error */ protected String getClassName(VirtualFile classFile) throws IOException { String pathName = classFile.getPathName(); String name = pathName.substring(rootLength, pathName.length()-6); name = name.replace('/', '.'); return name; } /** * Completely scan a class for annotations * @param cls * @return true if the class has annotations, false otherwise */ protected boolean hasAnnotations(Class<?> cls) { if(cls == null) return false; // Note: this also returns true if super class has annotations if(cls.getAnnotations().length > 0) return true; for(Method m : cls.getDeclaredMethods()) { if(m.getAnnotations().length > 0) return true; } for(Field f : cls.getDeclaredFields()) { if(f.getAnnotations().length > 0) return true; } return hasAnnotations(cls.getSuperclass()); } class NoChildFilter implements VirtualFileFilter { public boolean accepts(VirtualFile file) { String path = getFilePath(file); boolean accepts = false; try { accepts = file.isLeaf() == false && childPaths.contains(path) == false; } catch(Exception e) { } return accepts; } } }