You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2016/11/23 10:59:47 UTC
[1/2] struts git commit: WW-4570 Cleans up ASM and assemblies
Repository: struts
Updated Branches:
refs/heads/master 6f7fcaa33 -> 7872fa80f
WW-4570 Cleans up ASM and assemblies
Project: http://git-wip-us.apache.org/repos/asf/struts/repo
Commit: http://git-wip-us.apache.org/repos/asf/struts/commit/f03279fc
Tree: http://git-wip-us.apache.org/repos/asf/struts/tree/f03279fc
Diff: http://git-wip-us.apache.org/repos/asf/struts/diff/f03279fc
Branch: refs/heads/master
Commit: f03279fc1ae1524272b08a4d5e031f7304ea05c5
Parents: 6f7fcaa
Author: Lukasz Lenart <lu...@apache.org>
Authored: Wed Nov 23 11:43:05 2016 +0100
Committer: Lukasz Lenart <lu...@apache.org>
Committed: Wed Nov 23 11:43:05 2016 +0100
----------------------------------------------------------------------
assembly/src/main/assembly/all.xml | 1 +
assembly/src/main/assembly/lib.xml | 1 +
assembly/src/main/assembly/min-lib.xml | 5 +----
pom.xml | 7 +++----
4 files changed, 6 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/struts/blob/f03279fc/assembly/src/main/assembly/all.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/assembly/all.xml b/assembly/src/main/assembly/all.xml
index 8a05030..6d8fefb 100644
--- a/assembly/src/main/assembly/all.xml
+++ b/assembly/src/main/assembly/all.xml
@@ -31,6 +31,7 @@
</formats>
<dependencySets>
<dependencySet>
+ <useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
<excludes>
http://git-wip-us.apache.org/repos/asf/struts/blob/f03279fc/assembly/src/main/assembly/lib.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/assembly/lib.xml b/assembly/src/main/assembly/lib.xml
index 7aa42dd..e25418d 100644
--- a/assembly/src/main/assembly/lib.xml
+++ b/assembly/src/main/assembly/lib.xml
@@ -31,6 +31,7 @@
</formats>
<dependencySets>
<dependencySet>
+ <useProjectArtifact>false</useProjectArtifact>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
<excludes>
http://git-wip-us.apache.org/repos/asf/struts/blob/f03279fc/assembly/src/main/assembly/min-lib.xml
----------------------------------------------------------------------
diff --git a/assembly/src/main/assembly/min-lib.xml b/assembly/src/main/assembly/min-lib.xml
index 10a18ff..f139dd0 100644
--- a/assembly/src/main/assembly/min-lib.xml
+++ b/assembly/src/main/assembly/min-lib.xml
@@ -31,7 +31,7 @@
</formats>
<dependencySets>
<dependencySet>
- <useProjectArtifact>true</useProjectArtifact>
+ <useProjectArtifact>false</useProjectArtifact>
<useTransitiveDependencies>true</useTransitiveDependencies>
<outputDirectory>lib</outputDirectory>
<includes>
@@ -40,9 +40,6 @@
<include>org.apache.commons:commons-lang3</include>
<include>org.apache.logging.log4j:log4j-api</include>
<include>ognl:ognl</include>
- <include>org.ow2.asm:asm</include>
- <include>org.ow2.asm:asm-tree</include>
- <include>org.ow2.asm:asm-commons</include>
<include>org.javassist:javassist</include>
<include>commons-fileupload:commons-fileupload</include>
<include>commons-io:commons-io</include>
http://git-wip-us.apache.org/repos/asf/struts/blob/f03279fc/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 62f8db9..7e3ed60 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,8 +85,7 @@
<currentVersion>${project.version}</currentVersion>
<struts2.springPlatformVersion>4.1.6.RELEASE</struts2.springPlatformVersion>
<ognl.version>3.1.12</ognl.version>
- <asm.version>3.3</asm.version>
- <asm5.version>5.1</asm5.version>
+ <asm.version>5.1</asm.version>
<tiles.version>3.0.7</tiles.version>
<tiles-request.version>1.0.6</tiles-request.version>
<log4j2.version>2.7</log4j2.version>
@@ -547,12 +546,12 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
- <version>${asm5.version}</version>
+ <version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
- <version>${asm5.version}</version>
+ <version>${asm.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
[2/2] struts git commit: WW-4719 Moves DefaultClassFinder
implementation into Convention and ASM dependencies
Posted by lu...@apache.org.
WW-4719 Moves DefaultClassFinder implementation into Convention and ASM dependencies
Project: http://git-wip-us.apache.org/repos/asf/struts/repo
Commit: http://git-wip-us.apache.org/repos/asf/struts/commit/7872fa80
Tree: http://git-wip-us.apache.org/repos/asf/struts/tree/7872fa80
Diff: http://git-wip-us.apache.org/repos/asf/struts/diff/7872fa80
Branch: refs/heads/master
Commit: 7872fa80f957a3af72e760682c88a7283305f531
Parents: f03279f
Author: Lukasz Lenart <lu...@apache.org>
Authored: Wed Nov 23 11:59:32 2016 +0100
Committer: Lukasz Lenart <lu...@apache.org>
Committed: Wed Nov 23 11:59:32 2016 +0100
----------------------------------------------------------------------
core/pom.xml | 8 -
.../xwork2/util/finder/DefaultClassFinder.java | 580 ------------------
plugins/convention/pom.xml | 10 +
.../struts2/convention/DefaultClassFinder.java | 583 +++++++++++++++++++
4 files changed, 593 insertions(+), 588 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/struts/blob/7872fa80/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 9c59d91..cc2065b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -267,14 +267,6 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
- <groupId>org.ow2.asm</groupId>
- <artifactId>asm</artifactId>
- </dependency>
- <dependency>
- <groupId>org.ow2.asm</groupId>
- <artifactId>asm-commons</artifactId>
- </dependency>
- <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<optional>true</optional>
http://git-wip-us.apache.org/repos/asf/struts/blob/7872fa80/core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java b/core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java
deleted file mode 100644
index aed42a8..0000000
--- a/core/src/main/java/com/opensymphony/xwork2/util/finder/DefaultClassFinder.java
+++ /dev/null
@@ -1,580 +0,0 @@
-/*
- * Copyright 2002-2003,2009 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.opensymphony.xwork2.util.finder;
-
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.FileManager;
-import com.opensymphony.xwork2.FileManagerFactory;
-import com.opensymphony.xwork2.XWorkException;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.JarURLConnection;
-import java.net.URL;
-import java.net.URLDecoder;
-import java.util.*;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-
-public class DefaultClassFinder implements ClassFinder {
- private static final Logger LOG = LogManager.getLogger(DefaultClassFinder.class);
-
- private final Map<String, List<Info>> annotated = new HashMap<>();
- private final Map<String, ClassInfo> classInfos = new LinkedHashMap<>();
-
- private final List<String> classesNotLoaded = new ArrayList<>();
-
- private boolean extractBaseInterfaces;
- private ClassLoaderInterface classLoaderInterface;
- private FileManager fileManager;
-
- public DefaultClassFinder(ClassLoaderInterface classLoaderInterface, Collection<URL> urls, boolean extractBaseInterfaces, Set<String> protocols, Test<String> classNameFilter) {
- this.classLoaderInterface = classLoaderInterface;
- this.extractBaseInterfaces = extractBaseInterfaces;
- this.fileManager = ActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager();
-
- List<String> classNames = new ArrayList<>();
- for (URL location : urls) {
- try {
- if (protocols.contains(location.getProtocol())) {
- classNames.addAll(jar(location));
- } else if ("file".equals(location.getProtocol())) {
- try {
- // See if it's actually a jar
- URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/");
- JarURLConnection juc = (JarURLConnection) jarUrl.openConnection();
- juc.getJarFile();
- classNames.addAll(jar(jarUrl));
- } catch (IOException e) {
- classNames.addAll(file(location));
- }
- }
- } catch (Exception e) {
- LOG.error("Unable to read URL [{}]", location.toExternalForm(), e);
- }
- }
-
- for (String className : classNames) {
- try {
- if (classNameFilter.test(className))
- readClassDef(className);
- } catch (Throwable e) {
- LOG.error("Unable to read class [{}]", className, e);
- }
- }
- }
-
- public DefaultClassFinder(Class... classes){
- this(Arrays.asList(classes));
- }
-
- public DefaultClassFinder(List<Class> classes){
- this.classLoaderInterface = null;
- List<Info> infos = new ArrayList<>();
- List<Package> 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<Info> annotationInfos = getAnnotationInfos(annotation.getName());
- annotationInfos.add(info);
- }
- }
- }
-
- public ClassLoaderInterface getClassLoaderInterface() {
- return classLoaderInterface;
- }
-
- public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
- List<Info> infos = annotated.get(annotation.getName());
- return infos != null && !infos.isEmpty();
- }
-
- public List<String> getClassesNotLoaded() {
- return Collections.unmodifiableList(classesNotLoaded);
- }
-
- public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
- classesNotLoaded.clear();
- List<Package> packages = new ArrayList<>();
- List<Info> infos = getAnnotationInfos(annotation.getName());
- for (Info info : infos) {
- if (info instanceof PackageInfo) {
- PackageInfo packageInfo = (PackageInfo) info;
- try {
- Package pkg = packageInfo.get();
- // double check via proper reflection
- if (pkg.isAnnotationPresent(annotation)) {
- packages.add(pkg);
- }
- } catch (ClassNotFoundException e) {
- classesNotLoaded.add(packageInfo.getName());
- }
- }
- }
- return packages;
- }
-
- public List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation) {
- classesNotLoaded.clear();
- List<Class> classes = new ArrayList<>();
- List<Info> infos = getAnnotationInfos(annotation.getName());
- for (Info info : infos) {
- if (info instanceof ClassInfo) {
- ClassInfo classInfo = (ClassInfo) info;
- try {
- Class clazz = classInfo.get();
- // double check via proper reflection
- if (clazz.isAnnotationPresent(annotation)) {
- classes.add(clazz);
- }
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- }
- return classes;
- }
-
- public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
- classesNotLoaded.clear();
- List<ClassInfo> seen = new ArrayList<>();
- List<Method> methods = new ArrayList<>();
- List<Info> infos = getAnnotationInfos(annotation.getName());
- for (Info info : infos) {
- if (info instanceof MethodInfo && !"<init>".equals(info.getName())) {
- MethodInfo methodInfo = (MethodInfo) info;
- ClassInfo classInfo = methodInfo.getDeclaringClass();
-
- if (seen.contains(classInfo)) continue;
-
- seen.add(classInfo);
-
- try {
- Class clazz = classInfo.get();
- for (Method method : clazz.getDeclaredMethods()) {
- if (method.isAnnotationPresent(annotation)) {
- methods.add(method);
- }
- }
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- }
- return methods;
- }
-
- public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
- classesNotLoaded.clear();
- List<ClassInfo> seen = new ArrayList<>();
- List<Constructor> constructors = new ArrayList<>();
- List<Info> infos = getAnnotationInfos(annotation.getName());
- for (Info info : infos) {
- if (info instanceof MethodInfo && "<init>".equals(info.getName())) {
- MethodInfo methodInfo = (MethodInfo) info;
- ClassInfo classInfo = methodInfo.getDeclaringClass();
-
- if (seen.contains(classInfo)) continue;
-
- seen.add(classInfo);
-
- try {
- Class clazz = classInfo.get();
- for (Constructor constructor : clazz.getConstructors()) {
- if (constructor.isAnnotationPresent(annotation)) {
- constructors.add(constructor);
- }
- }
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- }
- return constructors;
- }
-
- public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
- classesNotLoaded.clear();
- List<ClassInfo> seen = new ArrayList<>();
- List<Field> fields = new ArrayList<>();
- List<Info> infos = getAnnotationInfos(annotation.getName());
- for (Info info : infos) {
- if (info instanceof FieldInfo) {
- FieldInfo fieldInfo = (FieldInfo) info;
- ClassInfo classInfo = fieldInfo.getDeclaringClass();
-
- if (seen.contains(classInfo)) {
- continue;
- }
-
- seen.add(classInfo);
-
- try {
- Class clazz = classInfo.get();
- for (Field field : clazz.getDeclaredFields()) {
- if (field.isAnnotationPresent(annotation)) {
- fields.add(field);
- }
- }
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- }
- return fields;
- }
-
- public List<Class> findClassesInPackage(String packageName, boolean recursive) {
- classesNotLoaded.clear();
- List<Class> classes = new ArrayList<>();
- for (ClassInfo classInfo : classInfos.values()) {
- try {
- if (recursive && classInfo.getPackageName().startsWith(packageName)){
- classes.add(classInfo.get());
- } else if (classInfo.getPackageName().equals(packageName)){
- classes.add(classInfo.get());
- }
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- return classes;
- }
-
- public List<Class> findClasses(Test<ClassInfo> test) {
- classesNotLoaded.clear();
- List<Class> classes = new ArrayList<>();
- for (ClassInfo classInfo : classInfos.values()) {
- try {
- if (test.test(classInfo)) {
- classes.add(classInfo.get());
- }
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- return classes;
- }
-
- public List<Class> findClasses() {
- classesNotLoaded.clear();
- List<Class> classes = new ArrayList<>();
- for (ClassInfo classInfo : classInfos.values()) {
- try {
- classes.add(classInfo.get());
- } catch (Throwable e) {
- LOG.error("Error loading class [{}]", classInfo.getName(), e);
- classesNotLoaded.add(classInfo.getName());
- }
- }
- return classes;
- }
-
- private static List<URL> getURLs(ClassLoaderInterface classLoader, String[] dirNames) {
- List<URL> urls = new ArrayList<>();
- for (String dirName : dirNames) {
- try {
- Enumeration<URL> classLoaderURLs = classLoader.getResources(dirName);
- while (classLoaderURLs.hasMoreElements()) {
- URL url = classLoaderURLs.nextElement();
- urls.add(url);
- }
- } catch (IOException ioe) {
- LOG.error("Could not read directory [{}]", dirName, ioe);
- }
- }
-
- return urls;
- }
-
- private List<String> file(URL location) {
- List<String> classNames = new ArrayList<>();
- File dir = new File(URLDecoder.decode(location.getPath()));
- if ("META-INF".equals(dir.getName())) {
- dir = dir.getParentFile(); // Scrape "META-INF" off
- }
- if (dir.isDirectory()) {
- scanDir(dir, classNames, "");
- }
- return classNames;
- }
-
- private void scanDir(File dir, List<String> 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);
- }
- }
- }
-
- private List<String> jar(URL location) throws IOException {
- URL url = fileManager.normalizeToFileProtocol(location);
- if (url != null) {
- InputStream in = url.openStream();
- try {
- JarInputStream jarStream = new JarInputStream(in);
- return jar(jarStream);
- } finally {
- in.close();
- }
- } else {
- LOG.debug("Unable to read [{}]", location.toExternalForm());
- }
- return Collections.emptyList();
- }
-
- private List<String> jar(JarInputStream jarStream) throws IOException {
- List<String> classNames = new ArrayList<>();
-
- JarEntry entry;
- while ((entry = jarStream.getNextJarEntry()) != null) {
- if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
- continue;
- }
- String className = entry.getName();
- className = className.replaceFirst(".class$", "");
-
- //war files are treated as .jar files, so takeout WEB-INF/classes
- className = StringUtils.removeStart(className, "WEB-INF/classes/");
-
- className = className.replace('/', '.');
- classNames.add(className);
- }
-
- 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<Info> getAnnotationInfos(String name) {
- List<Info> infos = annotated.get(name);
- if (infos == null) {
- infos = new ArrayList<>();
- annotated.put(name, infos);
- }
- return infos;
- }
-
- private void readClassDef(String className) {
- if (!className.endsWith(".class")) {
- className = className.replace('.', '/') + ".class";
- }
- try {
- URL resource = classLoaderInterface.getResource(className);
- if (resource != null) {
- try (InputStream in = resource.openStream()) {
- ClassReader classReader = new ClassReader(in);
- classReader.accept(new InfoBuildingVisitor(this), ClassReader.SKIP_DEBUG);
- }
- } else {
- throw new XWorkException("Could not load " + className);
- }
- } catch (IOException e) {
- throw new XWorkException("Could not load " + className, e);
- }
-
- }
-
- public class InfoBuildingVisitor extends ClassVisitor {
- private Info info;
- private ClassFinder classFinder;
-
- public InfoBuildingVisitor(ClassFinder classFinder) {
- super(Opcodes.ASM5);
- 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")) {
- info = new PackageInfo(javaName(name), classFinder);
- } else {
- ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName), classFinder);
-
- for (String interfce : interfaces) {
- classInfo.getInterfaces().add(javaName(interfce));
- }
- info = classInfo;
- classInfos.put(classInfo.getName(), classInfo);
-
- if (extractBaseInterfaces)
- extractSuperInterfaces(classInfo);
- }
- }
-
- private void extractSuperInterfaces(ClassInfo classInfo) {
- String superType = classInfo.getSuperType();
-
- if (superType != null) {
- ClassInfo base = classInfos.get(superType);
-
- if (base == null) {
- //try to load base
- String resource = superType.replace('.', '/') + ".class";
- readClassDef(resource);
- base = classInfos.get(superType);
- }
-
- if (base != null) {
- List<String> interfaces = classInfo.getSuperInterfaces();
- interfaces.addAll(base.getSuperInterfaces());
- interfaces.addAll(base.getInterfaces());
- }
- }
- }
-
- private String javaName(String name) {
- return (name == null) ? null : name.replace('/', '.');
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- AnnotationInfo annotationInfo = new AnnotationInfo(desc);
- info.getAnnotations().add(annotationInfo);
- getAnnotationInfos(annotationInfo.getName()).add(info);
- return null;
- }
-
- @Override
- public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
- ClassInfo classInfo = ((ClassInfo) info);
- FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
- classInfo.getFields().add(fieldInfo);
- return null;
- }
-
- @Override
- public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
- ClassInfo classInfo = ((ClassInfo) info);
- MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
- classInfo.getMethods().add(methodInfo);
- return new InfoBuildingMethodVisitor(methodInfo);
- }
- }
-
- public class InfoBuildingMethodVisitor extends MethodVisitor {
- private Info info;
-
- public InfoBuildingMethodVisitor() {
- super(Opcodes.ASM5);
- }
-
- public InfoBuildingMethodVisitor(Info info) {
- this();
- this.info = info;
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
- AnnotationInfo annotationInfo = new AnnotationInfo(desc);
- info.getAnnotations().add(annotationInfo);
- getAnnotationInfos(annotationInfo.getName()).add(info);
- return null;
- }
-
- @Override
- public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
- MethodInfo methodInfo = ((MethodInfo) info);
- List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
- AnnotationInfo annotationInfo = new AnnotationInfo(desc);
- annotationInfos.add(annotationInfo);
- return null;
- }
- }
-
-}
-
http://git-wip-us.apache.org/repos/asf/struts/blob/7872fa80/plugins/convention/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/convention/pom.xml b/plugins/convention/pom.xml
index 740934b..1995924 100644
--- a/plugins/convention/pom.xml
+++ b/plugins/convention/pom.xml
@@ -33,6 +33,16 @@
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-commons</artifactId>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
http://git-wip-us.apache.org/repos/asf/struts/blob/7872fa80/plugins/convention/src/main/java/org/apache/struts2/convention/DefaultClassFinder.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..9412f7e
--- /dev/null
+++ b/plugins/convention/src/main/java/org/apache/struts2/convention/DefaultClassFinder.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2002-2003,2009 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.struts2.convention;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.FileManager;
+import com.opensymphony.xwork2.FileManagerFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.util.finder.ClassFinder;
+import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
+import com.opensymphony.xwork2.util.finder.Test;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.*;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+public class DefaultClassFinder implements ClassFinder {
+ private static final Logger LOG = LogManager.getLogger(DefaultClassFinder.class);
+
+ private final Map<String, List<Info>> annotated = new HashMap<>();
+ private final Map<String, ClassInfo> classInfos = new LinkedHashMap<>();
+
+ private final List<String> classesNotLoaded = new ArrayList<>();
+
+ private boolean extractBaseInterfaces;
+ private ClassLoaderInterface classLoaderInterface;
+ private FileManager fileManager;
+
+ public DefaultClassFinder(ClassLoaderInterface classLoaderInterface, Collection<URL> urls, boolean extractBaseInterfaces, Set<String> protocols, Test<String> classNameFilter) {
+ this.classLoaderInterface = classLoaderInterface;
+ this.extractBaseInterfaces = extractBaseInterfaces;
+ this.fileManager = ActionContext.getContext().getInstance(FileManagerFactory.class).getFileManager();
+
+ List<String> classNames = new ArrayList<>();
+ for (URL location : urls) {
+ try {
+ if (protocols.contains(location.getProtocol())) {
+ classNames.addAll(jar(location));
+ } else if ("file".equals(location.getProtocol())) {
+ try {
+ // See if it's actually a jar
+ URL jarUrl = new URL("jar", "", location.toExternalForm() + "!/");
+ JarURLConnection juc = (JarURLConnection) jarUrl.openConnection();
+ juc.getJarFile();
+ classNames.addAll(jar(jarUrl));
+ } catch (IOException e) {
+ classNames.addAll(file(location));
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Unable to read URL [{}]", location.toExternalForm(), e);
+ }
+ }
+
+ for (String className : classNames) {
+ try {
+ if (classNameFilter.test(className))
+ readClassDef(className);
+ } catch (Throwable e) {
+ LOG.error("Unable to read class [{}]", className, e);
+ }
+ }
+ }
+
+ public DefaultClassFinder(Class... classes){
+ this(Arrays.asList(classes));
+ }
+
+ public DefaultClassFinder(List<Class> classes){
+ this.classLoaderInterface = null;
+ List<Info> infos = new ArrayList<>();
+ List<Package> 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<Info> annotationInfos = getAnnotationInfos(annotation.getName());
+ annotationInfos.add(info);
+ }
+ }
+ }
+
+ public ClassLoaderInterface getClassLoaderInterface() {
+ return classLoaderInterface;
+ }
+
+ public boolean isAnnotationPresent(Class<? extends Annotation> annotation) {
+ List<Info> infos = annotated.get(annotation.getName());
+ return infos != null && !infos.isEmpty();
+ }
+
+ public List<String> getClassesNotLoaded() {
+ return Collections.unmodifiableList(classesNotLoaded);
+ }
+
+ public List<Package> findAnnotatedPackages(Class<? extends Annotation> annotation) {
+ classesNotLoaded.clear();
+ List<Package> packages = new ArrayList<>();
+ List<Info> infos = getAnnotationInfos(annotation.getName());
+ for (Info info : infos) {
+ if (info instanceof PackageInfo) {
+ PackageInfo packageInfo = (PackageInfo) info;
+ try {
+ Package pkg = packageInfo.get();
+ // double check via proper reflection
+ if (pkg.isAnnotationPresent(annotation)) {
+ packages.add(pkg);
+ }
+ } catch (ClassNotFoundException e) {
+ classesNotLoaded.add(packageInfo.getName());
+ }
+ }
+ }
+ return packages;
+ }
+
+ public List<Class> findAnnotatedClasses(Class<? extends Annotation> annotation) {
+ classesNotLoaded.clear();
+ List<Class> classes = new ArrayList<>();
+ List<Info> infos = getAnnotationInfos(annotation.getName());
+ for (Info info : infos) {
+ if (info instanceof ClassInfo) {
+ ClassInfo classInfo = (ClassInfo) info;
+ try {
+ Class clazz = classInfo.get();
+ // double check via proper reflection
+ if (clazz.isAnnotationPresent(annotation)) {
+ classes.add(clazz);
+ }
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ }
+ return classes;
+ }
+
+ public List<Method> findAnnotatedMethods(Class<? extends Annotation> annotation) {
+ classesNotLoaded.clear();
+ List<ClassInfo> seen = new ArrayList<>();
+ List<Method> methods = new ArrayList<>();
+ List<Info> infos = getAnnotationInfos(annotation.getName());
+ for (Info info : infos) {
+ if (info instanceof MethodInfo && !"<init>".equals(info.getName())) {
+ MethodInfo methodInfo = (MethodInfo) info;
+ ClassInfo classInfo = methodInfo.getDeclaringClass();
+
+ if (seen.contains(classInfo)) continue;
+
+ seen.add(classInfo);
+
+ try {
+ Class clazz = classInfo.get();
+ for (Method method : clazz.getDeclaredMethods()) {
+ if (method.isAnnotationPresent(annotation)) {
+ methods.add(method);
+ }
+ }
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ }
+ return methods;
+ }
+
+ public List<Constructor> findAnnotatedConstructors(Class<? extends Annotation> annotation) {
+ classesNotLoaded.clear();
+ List<ClassInfo> seen = new ArrayList<>();
+ List<Constructor> constructors = new ArrayList<>();
+ List<Info> infos = getAnnotationInfos(annotation.getName());
+ for (Info info : infos) {
+ if (info instanceof MethodInfo && "<init>".equals(info.getName())) {
+ MethodInfo methodInfo = (MethodInfo) info;
+ ClassInfo classInfo = methodInfo.getDeclaringClass();
+
+ if (seen.contains(classInfo)) continue;
+
+ seen.add(classInfo);
+
+ try {
+ Class clazz = classInfo.get();
+ for (Constructor constructor : clazz.getConstructors()) {
+ if (constructor.isAnnotationPresent(annotation)) {
+ constructors.add(constructor);
+ }
+ }
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ }
+ return constructors;
+ }
+
+ public List<Field> findAnnotatedFields(Class<? extends Annotation> annotation) {
+ classesNotLoaded.clear();
+ List<ClassInfo> seen = new ArrayList<>();
+ List<Field> fields = new ArrayList<>();
+ List<Info> infos = getAnnotationInfos(annotation.getName());
+ for (Info info : infos) {
+ if (info instanceof FieldInfo) {
+ FieldInfo fieldInfo = (FieldInfo) info;
+ ClassInfo classInfo = fieldInfo.getDeclaringClass();
+
+ if (seen.contains(classInfo)) {
+ continue;
+ }
+
+ seen.add(classInfo);
+
+ try {
+ Class clazz = classInfo.get();
+ for (Field field : clazz.getDeclaredFields()) {
+ if (field.isAnnotationPresent(annotation)) {
+ fields.add(field);
+ }
+ }
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ }
+ return fields;
+ }
+
+ public List<Class> findClassesInPackage(String packageName, boolean recursive) {
+ classesNotLoaded.clear();
+ List<Class> classes = new ArrayList<>();
+ for (ClassInfo classInfo : classInfos.values()) {
+ try {
+ if (recursive && classInfo.getPackageName().startsWith(packageName)){
+ classes.add(classInfo.get());
+ } else if (classInfo.getPackageName().equals(packageName)){
+ classes.add(classInfo.get());
+ }
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ return classes;
+ }
+
+ public List<Class> findClasses(Test<ClassInfo> test) {
+ classesNotLoaded.clear();
+ List<Class> classes = new ArrayList<>();
+ for (ClassInfo classInfo : classInfos.values()) {
+ try {
+ if (test.test(classInfo)) {
+ classes.add(classInfo.get());
+ }
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ return classes;
+ }
+
+ public List<Class> findClasses() {
+ classesNotLoaded.clear();
+ List<Class> classes = new ArrayList<>();
+ for (ClassInfo classInfo : classInfos.values()) {
+ try {
+ classes.add(classInfo.get());
+ } catch (Throwable e) {
+ LOG.error("Error loading class [{}]", classInfo.getName(), e);
+ classesNotLoaded.add(classInfo.getName());
+ }
+ }
+ return classes;
+ }
+
+ private static List<URL> getURLs(ClassLoaderInterface classLoader, String[] dirNames) {
+ List<URL> urls = new ArrayList<>();
+ for (String dirName : dirNames) {
+ try {
+ Enumeration<URL> classLoaderURLs = classLoader.getResources(dirName);
+ while (classLoaderURLs.hasMoreElements()) {
+ URL url = classLoaderURLs.nextElement();
+ urls.add(url);
+ }
+ } catch (IOException ioe) {
+ LOG.error("Could not read directory [{}]", dirName, ioe);
+ }
+ }
+
+ return urls;
+ }
+
+ private List<String> file(URL location) {
+ List<String> classNames = new ArrayList<>();
+ File dir = new File(URLDecoder.decode(location.getPath()));
+ if ("META-INF".equals(dir.getName())) {
+ dir = dir.getParentFile(); // Scrape "META-INF" off
+ }
+ if (dir.isDirectory()) {
+ scanDir(dir, classNames, "");
+ }
+ return classNames;
+ }
+
+ private void scanDir(File dir, List<String> 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);
+ }
+ }
+ }
+
+ private List<String> jar(URL location) throws IOException {
+ URL url = fileManager.normalizeToFileProtocol(location);
+ if (url != null) {
+ InputStream in = url.openStream();
+ try {
+ JarInputStream jarStream = new JarInputStream(in);
+ return jar(jarStream);
+ } finally {
+ in.close();
+ }
+ } else {
+ LOG.debug("Unable to read [{}]", location.toExternalForm());
+ }
+ return Collections.emptyList();
+ }
+
+ private List<String> jar(JarInputStream jarStream) throws IOException {
+ List<String> classNames = new ArrayList<>();
+
+ JarEntry entry;
+ while ((entry = jarStream.getNextJarEntry()) != null) {
+ if (entry.isDirectory() || !entry.getName().endsWith(".class")) {
+ continue;
+ }
+ String className = entry.getName();
+ className = className.replaceFirst(".class$", "");
+
+ //war files are treated as .jar files, so takeout WEB-INF/classes
+ className = StringUtils.removeStart(className, "WEB-INF/classes/");
+
+ className = className.replace('/', '.');
+ classNames.add(className);
+ }
+
+ 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<Info> getAnnotationInfos(String name) {
+ List<Info> infos = annotated.get(name);
+ if (infos == null) {
+ infos = new ArrayList<>();
+ annotated.put(name, infos);
+ }
+ return infos;
+ }
+
+ private void readClassDef(String className) {
+ if (!className.endsWith(".class")) {
+ className = className.replace('.', '/') + ".class";
+ }
+ try {
+ URL resource = classLoaderInterface.getResource(className);
+ if (resource != null) {
+ try (InputStream in = resource.openStream()) {
+ ClassReader classReader = new ClassReader(in);
+ classReader.accept(new InfoBuildingVisitor(this), ClassReader.SKIP_DEBUG);
+ }
+ } else {
+ throw new XWorkException("Could not load " + className);
+ }
+ } catch (IOException e) {
+ throw new XWorkException("Could not load " + className, e);
+ }
+
+ }
+
+ public class InfoBuildingVisitor extends ClassVisitor {
+ private Info info;
+ private ClassFinder classFinder;
+
+ public InfoBuildingVisitor(ClassFinder classFinder) {
+ super(Opcodes.ASM5);
+ 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")) {
+ info = new PackageInfo(javaName(name), classFinder);
+ } else {
+ ClassInfo classInfo = new ClassInfo(javaName(name), javaName(superName), classFinder);
+
+ for (String interfce : interfaces) {
+ classInfo.getInterfaces().add(javaName(interfce));
+ }
+ info = classInfo;
+ classInfos.put(classInfo.getName(), classInfo);
+
+ if (extractBaseInterfaces)
+ extractSuperInterfaces(classInfo);
+ }
+ }
+
+ private void extractSuperInterfaces(ClassInfo classInfo) {
+ String superType = classInfo.getSuperType();
+
+ if (superType != null) {
+ ClassInfo base = classInfos.get(superType);
+
+ if (base == null) {
+ //try to load base
+ String resource = superType.replace('.', '/') + ".class";
+ readClassDef(resource);
+ base = classInfos.get(superType);
+ }
+
+ if (base != null) {
+ List<String> interfaces = classInfo.getSuperInterfaces();
+ interfaces.addAll(base.getSuperInterfaces());
+ interfaces.addAll(base.getInterfaces());
+ }
+ }
+ }
+
+ private String javaName(String name) {
+ return (name == null) ? null : name.replace('/', '.');
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ AnnotationInfo annotationInfo = new AnnotationInfo(desc);
+ info.getAnnotations().add(annotationInfo);
+ getAnnotationInfos(annotationInfo.getName()).add(info);
+ return null;
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+ ClassInfo classInfo = ((ClassInfo) info);
+ FieldInfo fieldInfo = new FieldInfo(classInfo, name, desc);
+ classInfo.getFields().add(fieldInfo);
+ return null;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+ ClassInfo classInfo = ((ClassInfo) info);
+ MethodInfo methodInfo = new MethodInfo(classInfo, name, desc);
+ classInfo.getMethods().add(methodInfo);
+ return new InfoBuildingMethodVisitor(methodInfo);
+ }
+ }
+
+ public class InfoBuildingMethodVisitor extends MethodVisitor {
+ private Info info;
+
+ public InfoBuildingMethodVisitor() {
+ super(Opcodes.ASM5);
+ }
+
+ public InfoBuildingMethodVisitor(Info info) {
+ this();
+ this.info = info;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ AnnotationInfo annotationInfo = new AnnotationInfo(desc);
+ info.getAnnotations().add(annotationInfo);
+ getAnnotationInfos(annotationInfo.getName()).add(info);
+ return null;
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int param, String desc, boolean visible) {
+ MethodInfo methodInfo = ((MethodInfo) info);
+ List<AnnotationInfo> annotationInfos = methodInfo.getParameterAnnotations(param);
+ AnnotationInfo annotationInfo = new AnnotationInfo(desc);
+ annotationInfos.add(annotationInfo);
+ return null;
+ }
+ }
+
+}
+