Issue #1901 Backport some java9 support to 9.3
This commit is contained in:
parent
8bcf10395d
commit
22f95ad847
|
@ -23,23 +23,23 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.jar.JarFile;
|
|
||||||
import java.util.jar.JarInputStream;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
|
||||||
import org.eclipse.jetty.util.Loader;
|
import org.eclipse.jetty.util.Loader;
|
||||||
import org.eclipse.jetty.util.MultiException;
|
import org.eclipse.jetty.util.MultiException;
|
||||||
import org.eclipse.jetty.util.MultiReleaseJarFile;
|
import org.eclipse.jetty.util.MultiReleaseJarFile;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.webapp.JarScanner;
|
|
||||||
import org.objectweb.asm.AnnotationVisitor;
|
import org.objectweb.asm.AnnotationVisitor;
|
||||||
import org.objectweb.asm.ClassReader;
|
import org.objectweb.asm.ClassReader;
|
||||||
import org.objectweb.asm.ClassVisitor;
|
import org.objectweb.asm.ClassVisitor;
|
||||||
|
@ -47,7 +47,6 @@ import org.objectweb.asm.FieldVisitor;
|
||||||
import org.objectweb.asm.MethodVisitor;
|
import org.objectweb.asm.MethodVisitor;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AnnotationParser
|
* AnnotationParser
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -70,30 +69,9 @@ public class AnnotationParser
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
|
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
|
||||||
|
|
||||||
private static final int JVM_MAJOR_VER;
|
|
||||||
|
|
||||||
protected Set<String> _parsedClassNames = new ConcurrentHashSet<String>();
|
|
||||||
|
|
||||||
protected static int ASM_OPCODE_VERSION = Opcodes.ASM5; //compatibility of api
|
protected static int ASM_OPCODE_VERSION = Opcodes.ASM5; //compatibility of api
|
||||||
|
|
||||||
static
|
protected Map<String, List<String>> _parsedClassNames = new ConcurrentHashMap<>();
|
||||||
{
|
|
||||||
// Determine JVM spec version
|
|
||||||
// Using guidance from http://openjdk.java.net/jeps/223
|
|
||||||
String jvmSpecVer = System.getProperty("java.vm.specification.version");
|
|
||||||
|
|
||||||
if (jvmSpecVer.indexOf('.') >= 0)
|
|
||||||
{
|
|
||||||
// Old spec version (Java 1.8 and older)
|
|
||||||
String parts[] = jvmSpecVer.split("\\.");
|
|
||||||
JVM_MAJOR_VER = Integer.parseInt(parts[1]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Newer spec version (Java 9+)
|
|
||||||
JVM_MAJOR_VER = Integer.parseInt(jvmSpecVer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert internal name to simple name
|
* Convert internal name to simple name
|
||||||
|
@ -132,12 +110,8 @@ public class AnnotationParser
|
||||||
return normalList;
|
return normalList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ClassInfo
|
|
||||||
*
|
|
||||||
* Immutable information gathered by parsing class header.
|
* Immutable information gathered by parsing class header.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class ClassInfo
|
public class ClassInfo
|
||||||
{
|
{
|
||||||
|
@ -197,10 +171,7 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MethodInfo
|
|
||||||
*
|
|
||||||
* Immutable information gathered by parsing a method on a class.
|
* Immutable information gathered by parsing a method on a class.
|
||||||
*/
|
*/
|
||||||
public class MethodInfo
|
public class MethodInfo
|
||||||
|
@ -253,14 +224,9 @@ public class AnnotationParser
|
||||||
return _exceptions;
|
return _exceptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldInfo
|
|
||||||
*
|
|
||||||
* Immutable information gathered by parsing a field on a class.
|
* Immutable information gathered by parsing a field on a class.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class FieldInfo
|
public class FieldInfo
|
||||||
{
|
{
|
||||||
|
@ -314,8 +280,6 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler
|
|
||||||
*
|
|
||||||
* Signature for all handlers that respond to parsing class files.
|
* Signature for all handlers that respond to parsing class files.
|
||||||
*/
|
*/
|
||||||
public static interface Handler
|
public static interface Handler
|
||||||
|
@ -329,13 +293,10 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractHandler
|
|
||||||
*
|
|
||||||
* Convenience base class to provide no-ops for all Handler methods.
|
* Convenience base class to provide no-ops for all Handler methods.
|
||||||
*/
|
*/
|
||||||
public static abstract class AbstractHandler implements Handler
|
public static abstract class AbstractHandler implements Handler
|
||||||
{
|
{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ClassInfo classInfo)
|
public void handle(ClassInfo classInfo)
|
||||||
{
|
{
|
||||||
|
@ -369,15 +330,11 @@ public class AnnotationParser
|
||||||
@Override
|
@Override
|
||||||
public void handle(FieldInfo info, String annotationName)
|
public void handle(FieldInfo info, String annotationName)
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MyMethodVisitor
|
|
||||||
*
|
|
||||||
* ASM Visitor for parsing a method. We are only interested in the annotations on methods.
|
* ASM Visitor for parsing a method. We are only interested in the annotations on methods.
|
||||||
*/
|
*/
|
||||||
public class MyMethodVisitor extends MethodVisitor
|
public class MyMethodVisitor extends MethodVisitor
|
||||||
|
@ -398,11 +355,8 @@ public class AnnotationParser
|
||||||
_mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions);
|
_mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We are only interested in finding the annotations on methods.
|
* We are only interested in finding the annotations on methods.
|
||||||
*
|
|
||||||
* @see org.objectweb.asm.MethodVisitor#visitAnnotation(java.lang.String, boolean)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||||
|
@ -414,21 +368,15 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MyFieldVisitor
|
* An ASM visitor for parsing Fields.
|
||||||
*
|
|
||||||
* An ASM visitor for parsing Fields.
|
|
||||||
* We are only interested in visiting annotations on Fields.
|
* We are only interested in visiting annotations on Fields.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class MyFieldVisitor extends FieldVisitor
|
public class MyFieldVisitor extends FieldVisitor
|
||||||
{
|
{
|
||||||
final FieldInfo _fieldInfo;
|
final FieldInfo _fieldInfo;
|
||||||
final Set<? extends Handler> _handlers;
|
final Set<? extends Handler> _handlers;
|
||||||
|
|
||||||
|
|
||||||
public MyFieldVisitor(final Set<? extends Handler> handlers,
|
public MyFieldVisitor(final Set<? extends Handler> handlers,
|
||||||
final ClassInfo classInfo,
|
final ClassInfo classInfo,
|
||||||
final int access,
|
final int access,
|
||||||
|
@ -442,11 +390,8 @@ public class AnnotationParser
|
||||||
_fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
|
_fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse an annotation found on a Field.
|
* Parse an annotation found on a Field.
|
||||||
*
|
|
||||||
* @see org.objectweb.asm.FieldVisitor#visitAnnotation(java.lang.String, boolean)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
|
||||||
|
@ -459,17 +404,11 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MyClassVisitor
|
|
||||||
*
|
|
||||||
* ASM visitor for a class.
|
* ASM visitor for a class.
|
||||||
*/
|
*/
|
||||||
public class MyClassVisitor extends ClassVisitor
|
public class MyClassVisitor extends ClassVisitor
|
||||||
{
|
{
|
||||||
|
|
||||||
final Resource _containingResource;
|
final Resource _containingResource;
|
||||||
final Set<? extends Handler> _handlers;
|
final Set<? extends Handler> _handlers;
|
||||||
ClassInfo _ci;
|
ClassInfo _ci;
|
||||||
|
@ -481,7 +420,6 @@ public class AnnotationParser
|
||||||
_containingResource = containingResource;
|
_containingResource = containingResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit (final int version,
|
public void visit (final int version,
|
||||||
final int access,
|
final int access,
|
||||||
|
@ -491,18 +429,12 @@ public class AnnotationParser
|
||||||
final String[] interfaces)
|
final String[] interfaces)
|
||||||
{
|
{
|
||||||
_ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces));
|
_ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces));
|
||||||
|
|
||||||
_parsedClassNames.add(_ci.getClassName());
|
|
||||||
|
|
||||||
for (Handler h:_handlers)
|
for (Handler h:_handlers)
|
||||||
h.handle(_ci);
|
h.handle(_ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit an annotation on a Class
|
* Visit an annotation on a Class
|
||||||
*
|
|
||||||
* @see org.objectweb.asm.ClassVisitor#visitAnnotation(java.lang.String, boolean)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public AnnotationVisitor visitAnnotation (String desc, boolean visible)
|
public AnnotationVisitor visitAnnotation (String desc, boolean visible)
|
||||||
|
@ -510,15 +442,11 @@ public class AnnotationParser
|
||||||
String annotationName = normalize(desc);
|
String annotationName = normalize(desc);
|
||||||
for (Handler h : _handlers)
|
for (Handler h : _handlers)
|
||||||
h.handle(_ci, annotationName);
|
h.handle(_ci, annotationName);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit a method to extract its annotations
|
* Visit a method to extract its annotations
|
||||||
*
|
|
||||||
* @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public MethodVisitor visitMethod (final int access,
|
public MethodVisitor visitMethod (final int access,
|
||||||
|
@ -527,14 +455,11 @@ public class AnnotationParser
|
||||||
final String signature,
|
final String signature,
|
||||||
final String[] exceptions)
|
final String[] exceptions)
|
||||||
{
|
{
|
||||||
|
|
||||||
return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions);
|
return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visit a field to extract its annotations
|
* Visit a field to extract its annotations
|
||||||
*
|
|
||||||
* @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public FieldVisitor visitField (final int access,
|
public FieldVisitor visitField (final int access,
|
||||||
|
@ -547,7 +472,25 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a class as having been parsed.
|
||||||
|
*
|
||||||
|
* @param classname the name of the class
|
||||||
|
* @param location the fully qualified location of the class
|
||||||
|
*/
|
||||||
|
public void addParsedClass (String classname, Resource location)
|
||||||
|
{
|
||||||
|
List<String> list = new ArrayList<>(1);
|
||||||
|
if (location != null)
|
||||||
|
list.add(location.toString());
|
||||||
|
|
||||||
|
List<String> existing = _parsedClassNames.putIfAbsent(classname, list);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
existing.addAll(list);
|
||||||
|
LOG.warn("{} scanned from multiple locations: {}", classname, existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the class has already been processed, false otherwise
|
* True if the class has already been processed, false otherwise
|
||||||
|
@ -556,11 +499,24 @@ public class AnnotationParser
|
||||||
*/
|
*/
|
||||||
public boolean isParsed (String className)
|
public boolean isParsed (String className)
|
||||||
{
|
{
|
||||||
return _parsedClassNames.contains(className);
|
return _parsedClassNames.containsKey(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the locations of the given classname. There may be more than one
|
||||||
|
* location if there are duplicates of the same class.
|
||||||
|
*
|
||||||
|
* @param classname the name of the class
|
||||||
|
* @return an immutable list of locations
|
||||||
|
*/
|
||||||
|
public List<String> getParsedLocations (String classname)
|
||||||
|
{
|
||||||
|
List<String> list = _parsedClassNames.get(classname);
|
||||||
|
if (list == null)
|
||||||
|
return Collections.emptyList();
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a given class
|
* Parse a given class
|
||||||
*
|
*
|
||||||
|
@ -577,24 +533,24 @@ public class AnnotationParser
|
||||||
|
|
||||||
if (!resolver.isExcluded(className))
|
if (!resolver.isExcluded(className))
|
||||||
{
|
{
|
||||||
if (!isParsed(className) || resolver.shouldOverride(className))
|
if (!isParsed(className) || resolver.shouldOverride(className))
|
||||||
{
|
{
|
||||||
className = className.replace('.', '/')+".class";
|
String tmp = className;
|
||||||
URL resource = Loader.getResource(this.getClass(), className);
|
className = className.replace('.', '/')+".class";
|
||||||
if (resource!= null)
|
URL resource = Loader.getResource(this.getClass(),className);
|
||||||
{
|
if (resource!= null)
|
||||||
Resource r = Resource.newResource(resource);
|
{
|
||||||
try (InputStream is = r.getInputStream())
|
Resource r = Resource.newResource(resource);
|
||||||
{
|
addParsedClass(tmp, r);
|
||||||
scanClass(handlers, null, is);
|
try (InputStream is = r.getInputStream())
|
||||||
}
|
{
|
||||||
}
|
scanClass(handlers, null, is);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the given class, optionally walking its inheritance hierarchy
|
* Parse the given class, optionally walking its inheritance hierarchy
|
||||||
*
|
*
|
||||||
|
@ -608,34 +564,33 @@ public class AnnotationParser
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
Class<?> cz = clazz;
|
Class<?> cz = clazz;
|
||||||
while (cz != null)
|
while (cz != Object.class)
|
||||||
{
|
{
|
||||||
if (!resolver.isExcluded(cz.getName()))
|
if (!resolver.isExcluded(cz.getName()))
|
||||||
{
|
{
|
||||||
if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
|
if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName()))
|
||||||
{
|
{
|
||||||
String nameAsResource = cz.getName().replace('.', '/')+".class";
|
String nameAsResource = cz.getName().replace('.', '/')+".class";
|
||||||
URL resource = Loader.getResource(this.getClass(), nameAsResource);
|
URL resource = Loader.getResource(this.getClass(),nameAsResource);
|
||||||
if (resource!= null)
|
if (resource!= null)
|
||||||
{
|
{
|
||||||
Resource r = Resource.newResource(resource);
|
Resource r = Resource.newResource(resource);
|
||||||
try (InputStream is = r.getInputStream())
|
addParsedClass(clazz.getName(), r);
|
||||||
{
|
try (InputStream is = r.getInputStream())
|
||||||
scanClass(handlers, null, is);
|
{
|
||||||
}
|
scanClass(handlers, null, is);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (visitSuperClasses)
|
if (visitSuperClasses)
|
||||||
cz = cz.getSuperclass();
|
cz = cz.getSuperclass();
|
||||||
else
|
else
|
||||||
cz = null;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the given classes
|
* Parse the given classes
|
||||||
*
|
*
|
||||||
|
@ -653,7 +608,6 @@ public class AnnotationParser
|
||||||
parse(handlers, Arrays.asList(classNames), resolver);
|
parse(handlers, Arrays.asList(classNames), resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the given classes
|
* Parse the given classes
|
||||||
*
|
*
|
||||||
|
@ -666,24 +620,26 @@ public class AnnotationParser
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
MultiException me = new MultiException();
|
MultiException me = new MultiException();
|
||||||
|
|
||||||
for (String s:classNames)
|
for (String s:classNames)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
|
if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s))))
|
||||||
{
|
{
|
||||||
s = s.replace('.', '/')+".class";
|
String name = s;
|
||||||
URL resource = Loader.getResource(this.getClass(), s);
|
s = s.replace('.', '/')+".class";
|
||||||
if (resource!= null)
|
URL resource = Loader.getResource(this.getClass(),s);
|
||||||
{
|
if (resource!= null)
|
||||||
Resource r = Resource.newResource(resource);
|
{
|
||||||
try (InputStream is = r.getInputStream())
|
Resource r = Resource.newResource(resource);
|
||||||
{
|
addParsedClass(name, r);
|
||||||
scanClass(handlers, null, is);
|
try (InputStream is = r.getInputStream())
|
||||||
}
|
{
|
||||||
}
|
scanClass(handlers, null, is);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -693,7 +649,6 @@ public class AnnotationParser
|
||||||
me.ifExceptionThrow();
|
me.ifExceptionThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse all classes in a directory
|
* Parse all classes in a directory
|
||||||
*
|
*
|
||||||
|
@ -702,59 +657,62 @@ public class AnnotationParser
|
||||||
* @param resolver the class name resolver
|
* @param resolver the class name resolver
|
||||||
* @throws Exception if unable to parse
|
* @throws Exception if unable to parse
|
||||||
*/
|
*/
|
||||||
protected void parseDir (Set<? extends Handler> handlers, Resource dir, ClassNameResolver resolver)
|
protected void parseDir (Set<? extends Handler> handlers, Resource root, ClassNameResolver resolver) throws Exception
|
||||||
throws Exception
|
|
||||||
{
|
{
|
||||||
// skip dirs whose name start with . (ie hidden)
|
if (!root.isDirectory() || !root.exists() || root.getName().startsWith("."))
|
||||||
if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Scanning dir {}", root);
|
||||||
MultiException me = new MultiException();
|
|
||||||
|
|
||||||
String[] files=dir.list();
|
File rootFile = root.getFile();
|
||||||
for (int f=0;files!=null && f<files.length;f++)
|
|
||||||
|
MultiException me = new MultiException();
|
||||||
|
Collection<Resource> resources = root.getAllResources();
|
||||||
|
if (resources != null)
|
||||||
{
|
{
|
||||||
Resource res = dir.addPath(files[f]);
|
for (Resource r:resources)
|
||||||
if (res.isDirectory())
|
|
||||||
parseDir(handlers, res, resolver);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
//we've already verified the directories, so just verify the class file name
|
if (r.isDirectory())
|
||||||
File file = res.getFile();
|
continue;
|
||||||
if (isValidClassFileName((file==null?null:file.getName())))
|
|
||||||
|
String name = r.getName();
|
||||||
|
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
||||||
{
|
{
|
||||||
try
|
File file = r.getFile();
|
||||||
{
|
if (isValidClassFileName((file==null?null:file.getName())))
|
||||||
String name = res.getName();
|
{
|
||||||
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
|
Path classpath = rootFile.toPath().relativize(file.toPath());
|
||||||
{
|
String str = classpath.toString();
|
||||||
Resource r = Resource.newResource(res.getURL());
|
str = str.substring(0, str.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
|
||||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
|
|
||||||
try (InputStream is=r.getInputStream())
|
try
|
||||||
{
|
{
|
||||||
scanClass(handlers, dir, is);
|
if (LOG.isDebugEnabled())
|
||||||
}
|
LOG.debug("Scanning class {}", r);
|
||||||
}
|
addParsedClass(str, r);
|
||||||
}
|
try (InputStream is=r.getInputStream())
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
scanClass(handlers, Resource.newResource(file.getParentFile()), is);
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+files[f], ex);
|
}
|
||||||
me.add(new RuntimeException("Error scanning file "+files[f],ex));
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
else
|
if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+file, ex);
|
||||||
{
|
me.add(new RuntimeException("Error scanning file "+file,ex));
|
||||||
if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", res);
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
me.ifExceptionThrow();
|
me.ifExceptionThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse classes in the supplied classloader.
|
* Parse classes in the supplied classloader.
|
||||||
* Only class files in jar files will be scanned.
|
* Only class files in jar files will be scanned.
|
||||||
|
@ -763,7 +721,6 @@ public class AnnotationParser
|
||||||
* @param loader the classloader for the classes
|
* @param loader the classloader for the classes
|
||||||
* @param visitParents if true, visit parent classloaders too
|
* @param visitParents if true, visit parent classloaders too
|
||||||
* @param nullInclusive if true, an empty pattern means all names match, if false, none match
|
* @param nullInclusive if true, an empty pattern means all names match, if false, none match
|
||||||
* @param resolver the class name resolver
|
|
||||||
* @throws Exception if unable to parse
|
* @throws Exception if unable to parse
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -772,7 +729,6 @@ public class AnnotationParser
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse classes in the supplied uris.
|
* Parse classes in the supplied uris.
|
||||||
*
|
*
|
||||||
|
@ -820,7 +776,6 @@ public class AnnotationParser
|
||||||
parse (handlers, Resource.newResource(uri), resolver);
|
parse (handlers, Resource.newResource(uri), resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a resource
|
* Parse a resource
|
||||||
*
|
*
|
||||||
|
@ -860,9 +815,6 @@ public class AnnotationParser
|
||||||
if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
|
if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a resource that is a jar file.
|
* Parse a resource that is a jar file.
|
||||||
*
|
*
|
||||||
|
@ -871,7 +823,7 @@ public class AnnotationParser
|
||||||
* @param resolver the class name resolver
|
* @param resolver the class name resolver
|
||||||
* @throws Exception if unable to parse
|
* @throws Exception if unable to parse
|
||||||
*/
|
*/
|
||||||
protected void parseJar (Set<? extends Handler> handlers, Resource jarResource, final ClassNameResolver resolver)
|
protected void parseJar (Set<? extends Handler> handlers, Resource jarResource, final ClassNameResolver resolver)
|
||||||
throws Exception
|
throws Exception
|
||||||
{
|
{
|
||||||
if (jarResource == null)
|
if (jarResource == null)
|
||||||
|
@ -879,15 +831,19 @@ public class AnnotationParser
|
||||||
|
|
||||||
if (jarResource.toString().endsWith(".jar"))
|
if (jarResource.toString().endsWith(".jar"))
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Scanning jar {}", jarResource);
|
||||||
|
|
||||||
MultiException me = new MultiException();
|
MultiException me = new MultiException();
|
||||||
JarFile jarFile = MultiReleaseJarFile.open(jarResource.getFile());
|
|
||||||
MultiReleaseJarFile.stream(jarFile).forEach(e->
|
// TODO do not force version 8 once ASM can scan 9
|
||||||
|
// TODO support a different target for quickstart generation
|
||||||
|
MultiReleaseJarFile jarFile = new MultiReleaseJarFile(jarResource.getFile(),8,false);
|
||||||
|
jarFile.stream().forEach(e->
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
parseJarEntry(handlers, jarResource, jarFile, e,resolver);
|
parseJarEntry(handlers, jarResource, e, resolver);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -895,10 +851,6 @@ public class AnnotationParser
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
jar_in.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
me.ifExceptionThrow();
|
me.ifExceptionThrow();
|
||||||
}
|
}
|
||||||
|
@ -908,14 +860,13 @@ public class AnnotationParser
|
||||||
* Parse a single entry in a jar file
|
* Parse a single entry in a jar file
|
||||||
*
|
*
|
||||||
* @param handlers the handlers to look for classes in
|
* @param handlers the handlers to look for classes in
|
||||||
* @param jarFile the jar resource being parses
|
* @param entry the entry in the potentially MultiRelease jar resource to parse
|
||||||
* @param entry the entry in the jar resource to parse
|
|
||||||
* @param resolver the class name resolver
|
* @param resolver the class name resolver
|
||||||
* @throws Exception if unable to parse
|
* @throws Exception if unable to parse
|
||||||
*/
|
*/
|
||||||
protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, JarFile jarFile, JarEntry entry,final ClassNameResolver resolver) throws Exception
|
protected void parseJarEntry (Set<? extends Handler> handlers, Resource jar, MultiReleaseJarFile.VersionedJarEntry entry,final ClassNameResolver resolver) throws Exception
|
||||||
{
|
{
|
||||||
if (jarFile == null || entry == null)
|
if (jar == null || entry == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//skip directories
|
//skip directories
|
||||||
|
@ -927,23 +878,21 @@ public class AnnotationParser
|
||||||
//check file is a valid class file name
|
//check file is a valid class file name
|
||||||
if (isValidClassFileName(name) && isValidClassFilePath(name))
|
if (isValidClassFileName(name) && isValidClassFilePath(name))
|
||||||
{
|
{
|
||||||
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
String shortName = name.replace('/', '.').substring(0,name.length()-6);
|
||||||
|
if ((resolver == null)
|
||||||
if ((resolver == null)
|
||
|
||||||
||
|
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
||||||
(!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName))))
|
{
|
||||||
{
|
addParsedClass(shortName, Resource.newResource("jar:"+jar.getURI()+"!/"+entry.getNameInJar()));
|
||||||
Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
|
if (LOG.isDebugEnabled())
|
||||||
if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
|
LOG.debug("Scanning class from jar {}!/{}", jar, entry);
|
||||||
try (InputStream is = jarFile.getInputStream(entry))
|
try (InputStream is = entry.getInputStream())
|
||||||
{
|
{
|
||||||
scanClass(handlers, jar, is);
|
scanClass(handlers, jar, is);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use ASM on a class
|
* Use ASM on a class
|
||||||
|
@ -953,13 +902,20 @@ public class AnnotationParser
|
||||||
* @param is the input stream to parse
|
* @param is the input stream to parse
|
||||||
* @throws IOException if unable to parse
|
* @throws IOException if unable to parse
|
||||||
*/
|
*/
|
||||||
protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is)
|
protected void scanClass (Set<? extends Handler> handlers, Resource containingResource, InputStream is) throws IOException
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
ClassReader reader = new ClassReader(is);
|
ClassReader reader = new ClassReader(is);
|
||||||
reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
|
reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any parsed class names.
|
||||||
|
*/
|
||||||
|
public void resetParsedClasses ()
|
||||||
|
{
|
||||||
|
_parsedClassNames.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the given path represents a valid class file name.
|
* Check that the given path represents a valid class file name.
|
||||||
* The check is fairly cursory, checking that:
|
* The check is fairly cursory, checking that:
|
||||||
|
@ -968,8 +924,8 @@ public class AnnotationParser
|
||||||
* <li> it isn't a dot file or in a hidden directory </li>
|
* <li> it isn't a dot file or in a hidden directory </li>
|
||||||
* <li> the name of the class at least begins with a valid identifier for a class name </li>
|
* <li> the name of the class at least begins with a valid identifier for a class name </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* @param name
|
* @param name the class file name
|
||||||
* @return
|
* @return whether the class file name is valid
|
||||||
*/
|
*/
|
||||||
private boolean isValidClassFileName (String name)
|
private boolean isValidClassFileName (String name)
|
||||||
{
|
{
|
||||||
|
@ -1003,13 +959,12 @@ public class AnnotationParser
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the given path does not contain hidden directories
|
* Check that the given path does not contain hidden directories
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path the class file path
|
||||||
* @return
|
* @return whether the class file path is valid
|
||||||
*/
|
*/
|
||||||
private boolean isValidClassFilePath (String path)
|
private boolean isValidClassFilePath (String path)
|
||||||
{
|
{
|
||||||
|
@ -1027,4 +982,3 @@ public class AnnotationParser
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ public class TestAnnotationParser
|
||||||
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/slf4j-api-1.8.0-alpha2.jar");
|
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/slf4j-api-1.8.0-alpha2.jar");
|
||||||
AnnotationParser parser = new AnnotationParser();
|
AnnotationParser parser = new AnnotationParser();
|
||||||
Set<Handler> emptySet = Collections.emptySet();
|
Set<Handler> emptySet = Collections.emptySet();
|
||||||
parser.parse(emptySet, badClassesJar.toURI());
|
parser.parse(emptySet, badClassesJar.toURI(),null);
|
||||||
// Should throw no exceptions, and happily skip the module-info.class files
|
// Should throw no exceptions, and happily skip the module-info.class files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ public class TestAnnotationParser
|
||||||
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/log4j-api-2.9.0.jar");
|
File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/log4j-api-2.9.0.jar");
|
||||||
AnnotationParser parser = new AnnotationParser();
|
AnnotationParser parser = new AnnotationParser();
|
||||||
Set<Handler> emptySet = Collections.emptySet();
|
Set<Handler> emptySet = Collections.emptySet();
|
||||||
parser.parse(emptySet, badClassesJar.toURI());
|
parser.parse(emptySet, badClassesJar.toURI(),null);
|
||||||
// Should throw no exceptions, and skip the META-INF/versions/9/* files
|
// Should throw no exceptions, and skip the META-INF/versions/9/* files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue