diff --git a/core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java b/core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java index fcbd08e4e..92e213655 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/finder/ClassFinder.java @@ -29,11 +29,11 @@ import java.util.List; /** * ClassFinder searches the classpath of the specified ClassLoaderInterface for * packages, classes, constructors, methods, or fields with specific annotations. - * + *

* For security reasons ASM is used to find the annotations. Classes are not * loaded unless they match the requirements of a called findAnnotated* method. * Once loaded, these classes are cached. - * + *

* The getClassesNotLoaded() method can be used immediately after any find* * method to get a list of classes which matched the find requirements (i.e. * contained the annotation), but were unable to be loaded. @@ -67,32 +67,32 @@ public interface ClassFinder { List findAnnotatedPackages(Class annotation); - List findAnnotatedClasses(Class annotation); + List> findAnnotatedClasses(Class annotation); List findAnnotatedMethods(Class annotation); - List findAnnotatedConstructors(Class annotation); + List> findAnnotatedConstructors(Class annotation); List findAnnotatedFields(Class annotation); - List findClassesInPackage(String packageName, boolean recursive); + List> findClassesInPackage(String packageName, boolean recursive); - List findClasses(Test test); + List> findClasses(Test test); - List findClasses(); + List> findClasses(); ClassLoaderInterface getClassLoaderInterface(); - public static interface Info { + interface Info { String getName(); List getAnnotations(); } - public class AnnotationInfo extends Annotatable implements Info { + class AnnotationInfo extends Annotatable implements Info { private final String name; - public AnnotationInfo(Annotation annotation){ + public AnnotationInfo(Annotation annotation) { this(annotation.getClass().getName()); } @@ -116,7 +116,7 @@ public interface ClassFinder { } } - public class Annotatable { + class Annotatable { private final List annotations = new ArrayList<>(); public Annotatable(AnnotatedElement element) { @@ -134,12 +134,12 @@ public interface ClassFinder { } - public class PackageInfo extends Annotatable implements Info { + class PackageInfo extends Annotatable implements Info { private final String name; private final ClassInfo info; private final Package pkg; - public PackageInfo(Package pkg){ + public PackageInfo(Package pkg) { super(pkg); this.pkg = pkg; this.name = pkg.getName(); @@ -157,11 +157,11 @@ public interface ClassFinder { } public Package get() throws ClassNotFoundException { - return (pkg != null)?pkg:info.get().getPackage(); + return (pkg != null) ? pkg : info.get().getPackage(); } } - public class ClassInfo extends Annotatable implements Info { + class ClassInfo extends Annotatable implements Info { private final String name; private final List methods = new ArrayList<>(); private final List constructors = new ArrayList<>(); @@ -169,17 +169,18 @@ public interface ClassFinder { private final List interfaces = new ArrayList<>(); private final List superInterfaces = new ArrayList<>(); private final List fields = new ArrayList<>(); + private final ClassFinder classFinder; + private Class clazz; - private ClassFinder classFinder; private ClassNotFoundException notFound; - public ClassInfo(Class clazz, ClassFinder classFinder) { + public ClassInfo(Class clazz, ClassFinder classFinder) { super(clazz); this.clazz = clazz; this.classFinder = classFinder; this.name = clazz.getName(); - Class superclass = clazz.getSuperclass(); - this.superType = superclass != null ? superclass.getName(): null; + Class superclass = clazz.getSuperclass(); + this.superType = superclass != null ? superclass.getName() : null; } public ClassInfo(String name, String superType, ClassFinder classFinder) { @@ -188,8 +189,8 @@ public interface ClassFinder { this.classFinder = classFinder; } - public String getPackageName(){ - return name.indexOf('.') > 0 ? name.substring(0, name.lastIndexOf('.')) : "" ; + public String getPackageName() { + return name.indexOf('.') > 0 ? name.substring(0, name.lastIndexOf('.')) : ""; } public List getConstructors() { @@ -220,7 +221,7 @@ public interface ClassFinder { return superType; } - public Class get() throws ClassNotFoundException { + public Class get() throws ClassNotFoundException { if (clazz != null) return clazz; if (notFound != null) throw notFound; try { @@ -239,20 +240,20 @@ public interface ClassFinder { } } - public class MethodInfo extends Annotatable implements Info { + class MethodInfo extends Annotatable implements Info { private final ClassInfo declaringClass; private final String returnType; private final String name; private final List> parameterAnnotations = new ArrayList<>(); - public MethodInfo(ClassInfo info, Constructor constructor){ + public MethodInfo(ClassInfo info, Constructor constructor) { super(constructor); this.declaringClass = info; this.name = ""; this.returnType = Void.TYPE.getName(); } - public MethodInfo(ClassInfo info, Method method){ + public MethodInfo(ClassInfo info, Method method) { super(method); this.declaringClass = info; this.name = method.getName(); @@ -297,12 +298,12 @@ public interface ClassFinder { } } - public class FieldInfo extends Annotatable implements Info { + class FieldInfo extends Annotatable implements Info { private final String name; private final String type; private final ClassInfo declaringClass; - public FieldInfo(ClassInfo info, Field field){ + public FieldInfo(ClassInfo info, Field field) { super(field); this.declaringClass = info; this.name = field.getName(); diff --git a/plugins/convention/src/main/java/org/apache/struts2/convention/DefaultClassFinder.java b/plugins/convention/src/main/java/org/apache/struts2/convention/DefaultClassFinder.java index 54d535811..9b6fdb86d 100644 --- a/plugins/convention/src/main/java/org/apache/struts2/convention/DefaultClassFinder.java +++ b/plugins/convention/src/main/java/org/apache/struts2/convention/DefaultClassFinder.java @@ -38,6 +38,7 @@ import org.objectweb.asm.Opcodes; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -45,7 +46,15 @@ import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; @@ -57,9 +66,9 @@ public class DefaultClassFinder implements ClassFinder { private final List classesNotLoaded = new ArrayList<>(); - private boolean extractBaseInterfaces; - private ClassLoaderInterface classLoaderInterface; - private FileManager fileManager; + private final ClassLoaderInterface classLoaderInterface; + private final boolean extractBaseInterfaces; + private final FileManager fileManager; public DefaultClassFinder(ClassLoaderInterface classLoaderInterface, Collection urls, boolean extractBaseInterfaces, Set protocols, Test classNameFilter) { this.classLoaderInterface = classLoaderInterface; @@ -97,46 +106,6 @@ public class DefaultClassFinder implements ClassFinder { } } - public DefaultClassFinder(Class... classes){ - this(Arrays.asList(classes)); - } - - public DefaultClassFinder(List classes){ - this.classLoaderInterface = null; - List infos = new ArrayList<>(); - List packages = new ArrayList<>(); - for (Class clazz : classes) { - - Package aPackage = clazz.getPackage(); - if (aPackage != null && !packages.contains(aPackage)){ - infos.add(new PackageInfo(aPackage)); - packages.add(aPackage); - } - - ClassInfo classInfo = new ClassInfo(clazz, this); - infos.add(classInfo); - classInfos.put(classInfo.getName(), classInfo); - for (Method method : clazz.getDeclaredMethods()) { - infos.add(new MethodInfo(classInfo, method)); - } - - for (Constructor constructor : clazz.getConstructors()) { - infos.add(new MethodInfo(classInfo, constructor)); - } - - for (Field field : clazz.getDeclaredFields()) { - infos.add(new FieldInfo(classInfo, field)); - } - } - - for (Info info : infos) { - for (AnnotationInfo annotation : info.getAnnotations()) { - List annotationInfos = getAnnotationInfos(annotation.getName()); - annotationInfos.add(info); - } - } - } - public ClassLoaderInterface getClassLoaderInterface() { return classLoaderInterface; } @@ -171,15 +140,15 @@ public class DefaultClassFinder implements ClassFinder { return packages; } - public List findAnnotatedClasses(Class annotation) { + public List> findAnnotatedClasses(Class annotation) { classesNotLoaded.clear(); - List classes = new ArrayList<>(); + List> classes = new ArrayList<>(); List infos = getAnnotationInfos(annotation.getName()); for (Info info : infos) { if (info instanceof ClassInfo) { ClassInfo classInfo = (ClassInfo) info; try { - Class clazz = classInfo.get(); + Class clazz = classInfo.get(); // double check via proper reflection if (clazz.isAnnotationPresent(annotation)) { classes.add(clazz); @@ -208,7 +177,7 @@ public class DefaultClassFinder implements ClassFinder { seen.add(classInfo); try { - Class clazz = classInfo.get(); + Class clazz = classInfo.get(); for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(annotation)) { methods.add(method); @@ -223,10 +192,10 @@ public class DefaultClassFinder implements ClassFinder { return methods; } - public List findAnnotatedConstructors(Class annotation) { + public List> findAnnotatedConstructors(Class annotation) { classesNotLoaded.clear(); List seen = new ArrayList<>(); - List constructors = new ArrayList<>(); + List> constructors = new ArrayList<>(); List infos = getAnnotationInfos(annotation.getName()); for (Info info : infos) { if (info instanceof MethodInfo && "".equals(info.getName())) { @@ -238,8 +207,8 @@ public class DefaultClassFinder implements ClassFinder { seen.add(classInfo); try { - Class clazz = classInfo.get(); - for (Constructor constructor : clazz.getConstructors()) { + Class clazz = classInfo.get(); + for (Constructor constructor : clazz.getConstructors()) { if (constructor.isAnnotationPresent(annotation)) { constructors.add(constructor); } @@ -270,7 +239,7 @@ public class DefaultClassFinder implements ClassFinder { seen.add(classInfo); try { - Class clazz = classInfo.get(); + Class clazz = classInfo.get(); for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(annotation)) { fields.add(field); @@ -285,14 +254,14 @@ public class DefaultClassFinder implements ClassFinder { return fields; } - public List findClassesInPackage(String packageName, boolean recursive) { + public List> findClassesInPackage(String packageName, boolean recursive) { classesNotLoaded.clear(); - List classes = new ArrayList<>(); + List> classes = new ArrayList<>(); for (ClassInfo classInfo : classInfos.values()) { try { - if (recursive && classInfo.getPackageName().startsWith(packageName)){ + if (recursive && classInfo.getPackageName().startsWith(packageName)) { classes.add(classInfo.get()); - } else if (classInfo.getPackageName().equals(packageName)){ + } else if (classInfo.getPackageName().equals(packageName)) { classes.add(classInfo.get()); } } catch (Throwable e) { @@ -303,9 +272,9 @@ public class DefaultClassFinder implements ClassFinder { return classes; } - public List findClasses(Test test) { + public List> findClasses(Test test) { classesNotLoaded.clear(); - List classes = new ArrayList<>(); + List> classes = new ArrayList<>(); for (ClassInfo classInfo : classInfos.values()) { try { if (test.test(classInfo)) { @@ -319,9 +288,9 @@ public class DefaultClassFinder implements ClassFinder { return classes; } - public List findClasses() { + public List> findClasses() { classesNotLoaded.clear(); - List classes = new ArrayList<>(); + List> classes = new ArrayList<>(); for (ClassInfo classInfo : classInfos.values()) { try { classes.add(classInfo.get()); @@ -333,9 +302,9 @@ public class DefaultClassFinder implements ClassFinder { return classes; } - private List file(URL location) { + private List file(URL location) throws UnsupportedEncodingException { List classNames = new ArrayList<>(); - File dir = new File(URLDecoder.decode(location.getPath())); + File dir = new File(URLDecoder.decode(location.getPath(), "UTF-8")); if ("META-INF".equals(dir.getName())) { dir = dir.getParentFile(); // Scrape "META-INF" off } @@ -347,15 +316,17 @@ public class DefaultClassFinder implements ClassFinder { private void scanDir(File dir, List classNames, String packageName) { File[] files = dir.listFiles(); - for (File file : files) { - if (file.isDirectory()) { - scanDir(file, classNames, packageName + file.getName() + "."); - } else if (file.getName().endsWith(".class")) { - String name = file.getName(); - name = name.replaceFirst(".class$", ""); - // Classes packaged in an exploded .war (e.g. in a VFS file system) should not - // have WEB-INF.classes in their package name. - classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + scanDir(file, classNames, packageName + file.getName() + "."); + } else if (file.getName().endsWith(".class")) { + String name = file.getName(); + name = name.replaceFirst(".class$", ""); + // Classes packaged in an exploded .war (e.g. in a VFS file system) should not + // have WEB-INF.classes in their package name. + classNames.add(StringUtils.removeStart(packageName, "WEB-INF.classes.") + name); + } } } } @@ -363,12 +334,9 @@ public class DefaultClassFinder implements ClassFinder { private List jar(URL location) throws IOException { URL url = fileManager.normalizeToFileProtocol(location); if (url != null) { - InputStream in = url.openStream(); - try { + try (InputStream in = url.openStream()) { JarInputStream jarStream = new JarInputStream(in); return jar(jarStream); - } finally { - in.close(); } } else { LOG.debug("Unable to read [{}]", location.toExternalForm()); @@ -388,7 +356,7 @@ public class DefaultClassFinder implements ClassFinder { className = className.replaceFirst(".class$", ""); //war files are treated as .jar files, so takeout WEB-INF/classes - className = StringUtils.removeStart(className, "WEB-INF/classes/"); + className = StringUtils.removeStart(className, "WEB-INF/classes/"); className = className.replace('/', '.'); classNames.add(className); @@ -397,40 +365,8 @@ public class DefaultClassFinder implements ClassFinder { return classNames; } - public class PackageInfo extends Annotatable implements Info { - private final String name; - private final ClassInfo info; - private final Package pkg; - - public PackageInfo(Package pkg){ - super(pkg); - this.pkg = pkg; - this.name = pkg.getName(); - this.info = null; - } - - public PackageInfo(String name, ClassFinder classFinder) { - info = new ClassInfo(name, null, classFinder); - this.name = name; - this.pkg = null; - } - - public String getName() { - return name; - } - - public Package get() throws ClassNotFoundException { - return (pkg != null)?pkg:info.get().getPackage(); - } - } - private List getAnnotationInfos(String name) { - List infos = annotated.get(name); - if (infos == null) { - infos = new ArrayList<>(); - annotated.put(name, infos); - } - return infos; + return annotated.computeIfAbsent(name, k -> new ArrayList<>()); } private void readClassDef(String className) { @@ -454,19 +390,16 @@ public class DefaultClassFinder implements ClassFinder { } public class InfoBuildingVisitor extends ClassVisitor { + + private final ClassFinder classFinder; + private Info info; - private ClassFinder classFinder; public InfoBuildingVisitor(ClassFinder classFinder) { - super(Opcodes.ASM7); + super(Opcodes.ASM9); this.classFinder = classFinder; } - public InfoBuildingVisitor(Info info, ClassFinder classFinder) { - this(classFinder); - this.info = info; - } - @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if (name.endsWith("package-info")) { @@ -480,8 +413,9 @@ public class DefaultClassFinder implements ClassFinder { info = classInfo; classInfos.put(classInfo.getName(), classInfo); - if (extractBaseInterfaces) + if (extractBaseInterfaces) { extractSuperInterfaces(classInfo); + } } }