diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java index dc0556a30056..d07eacc47bf2 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java @@ -19,6 +19,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Locale; +import java.util.Objects; import java.util.regex.Pattern; /** @@ -38,6 +39,7 @@ public class FileID */ public static String getBasename(Path path) { + Objects.requireNonNull(path); Path filename = path.getFileName(); if (filename == null) return ""; @@ -366,11 +368,27 @@ public static boolean isLibArchive(URI uri) */ public static boolean isClassFile(Path path) { + if (path == null) + return false; + Path fileNamePath = path.getFileName(); if (fileNamePath == null) return false; - - String filename = fileNamePath.toString(); + + return isClassFile(fileNamePath.toString()); + } + + /** + * Predicate to test for class files + * + * @param filename the filename to test + * @return true if the filename ends with {@code .class} + */ + public static boolean isClassFile(String filename) + { + if (StringUtil.isBlank(filename)) + return false; + // has to end in ".class" if (!StringUtil.asciiEndsWithIgnoreCase(filename, ".class")) return false; @@ -404,6 +422,8 @@ public static boolean isClassFile(Path path) */ public static boolean isHidden(Path path) { + Objects.requireNonNull(path); + int count = path.getNameCount(); for (int i = 0; i < count; i++) { @@ -443,6 +463,9 @@ public static boolean isHidden(Path path) */ public static boolean isHidden(Path base, Path path) { + Objects.requireNonNull(base); + Objects.requireNonNull(path); + // Work with the path in relative form, from the base onwards to the path return isHidden(base.relativize(path)); } @@ -492,6 +515,9 @@ public static boolean isJavaArchive(String filename) */ public static boolean isMetaInfVersions(Path path) { + if (path == null) + return false; + if (path.getNameCount() < 3) return false; @@ -531,6 +557,9 @@ public static boolean isNotMetaInfVersions(Path path) */ public static boolean isModuleInfoClass(Path path) { + if (path == null) + return false; + Path filenameSegment = path.getFileName(); if (filenameSegment == null) return false; diff --git a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java index 4a9c6ecf6052..c9be26429469 100644 --- a/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java +++ b/jetty-ee10/jetty-ee10-annotations/src/main/java/org/eclipse/jetty/ee10/annotations/AnnotationParser.java @@ -21,15 +21,18 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.jetty.io.IOResources; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.Resources; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -550,21 +553,23 @@ public void parse(final Set handlers, Resource r) throws Exce if (!r.exists()) return; - if (FileID.isJavaArchive(r.getPath())) - { - parseJar(handlers, r); - return; - } - if (r.isDirectory()) { parseDir(handlers, r); return; } - - if (FileID.isClassFile(r.getPath())) + else { - parseClass(handlers, null, r.getPath()); + if (FileID.isJavaArchive(r.getFileName())) + { + parseJar(handlers, r); + return; + } + + if (FileID.isClassFile(r.getFileName())) + { + parseClass(handlers, null, r); + } } if (LOG.isDebugEnabled()) @@ -580,15 +585,23 @@ public void parse(final Set handlers, Resource r) throws Exce */ protected void parseDir(Set handlers, Resource dirResource) throws Exception { + Objects.requireNonNull(handlers); + Objects.requireNonNull(dirResource); + if (LOG.isDebugEnabled()) LOG.debug("Scanning dir {}", dirResource); - assert dirResource.isDirectory(); + if (!Resources.isDirectory(dirResource)) + return; ExceptionUtil.MultiException multiException = new ExceptionUtil.MultiException(); for (Resource candidate : dirResource.getAllResources()) { + // Skip ones that don't exist + if (!Resources.exists(candidate)) + continue; + // Skip directories if (candidate.isDirectory()) continue; @@ -606,7 +619,7 @@ protected void parseDir(Set handlers, Resource dirResource) t try { - parseClass(handlers, dirResource, candidate.getPath()); + parseClass(handlers, dirResource, candidate); } catch (Exception ex) { @@ -626,15 +639,14 @@ protected void parseDir(Set handlers, Resource dirResource) t */ protected void parseJar(Set handlers, Resource jarResource) throws Exception { - if (jarResource == null) - return; - - /* if (!FileID.isJavaArchive(jarResource.getPath())) - return;*/ + Objects.requireNonNull(jarResource); if (LOG.isDebugEnabled()) LOG.debug("Scanning jar {}", jarResource); + if (!Resources.isReadableFile(jarResource)) + return; + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) { Resource insideJarResource = resourceFactory.newJarFileResource(jarResource.getURI()); @@ -649,27 +661,75 @@ protected void parseJar(Set handlers, Resource jarResource) t * @param containingResource the dir or jar that the class is contained within, can be null if not known * @param classFile the class file to parse * @throws IOException if unable to parse + * @deprecated use {@link #parseClass(Set, Resource, Resource)} instead (which uses {@link Resource} instead of {@link Path}) */ + @Deprecated(since = "12.0.16", forRemoval = true) protected void parseClass(Set handlers, Resource containingResource, Path classFile) throws IOException { - if (LOG.isDebugEnabled()) - LOG.debug("Parse class from {}", classFile.toUri()); + Objects.requireNonNull(classFile); + + if (!(Files.isRegularFile(classFile) && Files.isReadable(classFile))) + return; + + try (InputStream inputStream = Files.newInputStream(classFile)) + { + parseClass(handlers, containingResource, classFile.toUri(), inputStream); + } + } + + /** + * Use ASM on a class + * + * @param handlers the handlers to look for classes in + * @param containingResource the dir or jar that the class is contained within, can be null if not known + * @param classFile the class file to parse + * @throws IOException if unable to parse + */ + protected void parseClass(Set handlers, Resource containingResource, Resource classFile) throws IOException + { + Objects.requireNonNull(classFile); + + if (!Resources.isReadableFile(classFile)) + return; + + try (InputStream inputStream = IOResources.asInputStream(classFile)) + { + parseClass(handlers, containingResource, classFile.getURI(), inputStream); + } + } - URI location = classFile.toUri(); + /** + * Use ASM on a class + * + * @param handlers the handlers to look for classes in + * @param containingResource the dir or jar that the class is contained within, can be null if not known + * @param classFileRef the URI reference to the classfile location + * @param inputStream the class file contents to parse + * @throws IOException if unable to parse + */ + private void parseClass(Set handlers, Resource containingResource, URI classFileRef, InputStream inputStream) throws IOException + { + Objects.requireNonNull(handlers); + Objects.requireNonNull(containingResource); + Objects.requireNonNull(classFileRef); + Objects.requireNonNull(inputStream); - try (InputStream in = Files.newInputStream(classFile)) + if (LOG.isDebugEnabled()) + LOG.debug("Parse class from {}", classFileRef); + + try { - ClassReader reader = new ClassReader(in); + ClassReader reader = new ClassReader(inputStream); reader.accept(new MyClassVisitor(handlers, containingResource, _asmVersion), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); String classname = normalize(reader.getClassName()); - URI existing = _parsedClassNames.putIfAbsent(classname, location); + URI existing = _parsedClassNames.putIfAbsent(classname, classFileRef); if (existing != null) - LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, location); + LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, classFileRef); } catch (IllegalArgumentException | IOException e) { - throw new IOException("Unable to parse class: " + classFile.toUri(), e); + throw new IOException("Unable to parse class: " + classFileRef, e); } } diff --git a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java index 83e1c2f09ca1..567e0722b826 100644 --- a/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java +++ b/jetty-ee10/jetty-ee10-osgi/jetty-ee10-osgi-boot/src/main/java/org/eclipse/jetty/ee10/osgi/annotations/AnnotationParser.java @@ -88,7 +88,6 @@ protected Bundle getBundle(Resource resource) public void parse(Set handlers, Bundle bundle) throws Exception { - Resource bundleResource = _bundleToResource.get(bundle); if (bundleResource == null) return; @@ -109,25 +108,28 @@ public void parse(final Set handlers, Resource r) throws Exce if (!r.exists()) return; - if (FileID.isJavaArchive(r.getPath())) - { - parseJar(handlers, r); - return; - } - if (r.isDirectory()) { parseDir(handlers, r); return; } - - if (FileID.isClassFile(r.getPath())) + else { - parseClass(handlers, null, r.getPath()); + if (FileID.isJavaArchive(r.getFileName())) + { + parseJar(handlers, r); + return; + } + + if (FileID.isClassFile(r.getFileName())) + { + parseClass(handlers, null, r); + return; + } } - //Not already parsed, it could be a file that actually is compressed but does not have - //.jar/.zip etc extension, such as equinox urls, so try to parse it + // Not already parsed, it could be a file that actually is compressed but does not have + // .jar/.zip etc extension, such as equinox urls, so try to parse it try { parseJar(handlers, r); diff --git a/jetty-ee9/jetty-ee9-annotations/src/main/java/org/eclipse/jetty/ee9/annotations/AnnotationParser.java b/jetty-ee9/jetty-ee9-annotations/src/main/java/org/eclipse/jetty/ee9/annotations/AnnotationParser.java index eed4f576965e..97cd517e846a 100644 --- a/jetty-ee9/jetty-ee9-annotations/src/main/java/org/eclipse/jetty/ee9/annotations/AnnotationParser.java +++ b/jetty-ee9/jetty-ee9-annotations/src/main/java/org/eclipse/jetty/ee9/annotations/AnnotationParser.java @@ -21,15 +21,18 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.jetty.io.IOResources; import org.eclipse.jetty.util.ExceptionUtil; import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.Resources; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -547,21 +550,26 @@ public void parse(final Set handlers, Resource r) throws Exce if (r == null) return; - if (FileID.isJavaArchive(r.getPath())) - { - parseJar(handlers, r); + if (!r.exists()) return; - } if (r.isDirectory()) { parseDir(handlers, r); return; } - - if (FileID.isClassFile(r.getPath())) + else { - parseClass(handlers, null, r.getPath()); + if (FileID.isJavaArchive(r.getFileName())) + { + parseJar(handlers, r); + return; + } + + if (FileID.isClassFile(r.getFileName())) + { + parseClass(handlers, null, r); + } } if (LOG.isDebugEnabled()) @@ -577,15 +585,23 @@ public void parse(final Set handlers, Resource r) throws Exce */ protected void parseDir(Set handlers, Resource dirResource) throws Exception { + Objects.requireNonNull(handlers); + Objects.requireNonNull(dirResource); + if (LOG.isDebugEnabled()) LOG.debug("Scanning dir {}", dirResource); - assert dirResource.isDirectory(); + if (!Resources.isDirectory(dirResource)) + return; ExceptionUtil.MultiException multiException = new ExceptionUtil.MultiException(); for (Resource candidate : dirResource.getAllResources()) { + // Skip ones that don't exist + if (!Resources.exists(candidate)) + continue; + // Skip directories if (candidate.isDirectory()) continue; @@ -603,7 +619,7 @@ protected void parseDir(Set handlers, Resource dirResource) t try { - parseClass(handlers, dirResource, candidate.getPath()); + parseClass(handlers, dirResource, candidate); } catch (Exception ex) { @@ -623,15 +639,14 @@ protected void parseDir(Set handlers, Resource dirResource) t */ protected void parseJar(Set handlers, Resource jarResource) throws Exception { - if (jarResource == null) - return; - - /* if (!FileID.isJavaArchive(jarResource.getPath())) - return;*/ + Objects.requireNonNull(jarResource); if (LOG.isDebugEnabled()) LOG.debug("Scanning jar {}", jarResource); + if (!Resources.isReadableFile(jarResource)) + return; + try (ResourceFactory.Closeable resourceFactory = ResourceFactory.closeable()) { Resource insideJarResource = resourceFactory.newJarFileResource(jarResource.getURI()); @@ -646,27 +661,75 @@ protected void parseJar(Set handlers, Resource jarResource) t * @param containingResource the dir or jar that the class is contained within, can be null if not known * @param classFile the class file to parse * @throws IOException if unable to parse + * @deprecated use {@link #parseClass(Set, Resource, Resource)} instead (which uses {@link Resource} instead of {@link Path}) */ + @Deprecated(since = "12.0.16", forRemoval = true) protected void parseClass(Set handlers, Resource containingResource, Path classFile) throws IOException { - if (LOG.isDebugEnabled()) - LOG.debug("Parse class from {}", classFile.toUri()); + Objects.requireNonNull(classFile); + + if (!(Files.isRegularFile(classFile) && Files.isReadable(classFile))) + return; + + try (InputStream inputStream = Files.newInputStream(classFile)) + { + parseClass(handlers, containingResource, classFile.toUri(), inputStream); + } + } + + /** + * Use ASM on a class + * + * @param handlers the handlers to look for classes in + * @param containingResource the dir or jar that the class is contained within, can be null if not known + * @param classFile the class file to parse + * @throws IOException if unable to parse + */ + protected void parseClass(Set handlers, Resource containingResource, Resource classFile) throws IOException + { + Objects.requireNonNull(classFile); + + if (!Resources.isReadableFile(classFile)) + return; - URI location = classFile.toUri(); + try (InputStream inputStream = IOResources.asInputStream(classFile)) + { + parseClass(handlers, containingResource, classFile.getURI(), inputStream); + } + } - try (InputStream in = Files.newInputStream(classFile)) + /** + * Use ASM on a class + * + * @param handlers the handlers to look for classes in + * @param containingResource the dir or jar that the class is contained within, can be null if not known + * @param classFileRef the URI reference to the classfile location + * @param inputStream the class file contents to parse + * @throws IOException if unable to parse + */ + private void parseClass(Set handlers, Resource containingResource, URI classFileRef, InputStream inputStream) throws IOException + { + Objects.requireNonNull(handlers); + Objects.requireNonNull(containingResource); + Objects.requireNonNull(classFileRef); + Objects.requireNonNull(inputStream); + + if (LOG.isDebugEnabled()) + LOG.debug("Parse class from {}", classFileRef); + + try { - ClassReader reader = new ClassReader(in); + ClassReader reader = new ClassReader(inputStream); reader.accept(new MyClassVisitor(handlers, containingResource, _asmVersion), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); String classname = normalize(reader.getClassName()); - URI existing = _parsedClassNames.putIfAbsent(classname, location); + URI existing = _parsedClassNames.putIfAbsent(classname, classFileRef); if (existing != null) - LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, location); + LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, classFileRef); } catch (IllegalArgumentException | IOException e) { - throw new IOException("Unable to parse class: " + classFile.toUri(), e); + throw new IOException("Unable to parse class: " + classFileRef, e); } } diff --git a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/annotations/AnnotationParser.java b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/annotations/AnnotationParser.java index 312f21b274db..2d3593a4ccac 100644 --- a/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/annotations/AnnotationParser.java +++ b/jetty-ee9/jetty-ee9-osgi/jetty-ee9-osgi-boot/src/main/java/org/eclipse/jetty/ee9/osgi/annotations/AnnotationParser.java @@ -14,23 +14,15 @@ package org.eclipse.jetty.ee9.osgi.annotations; import java.io.File; -import java.io.InputStream; import java.net.URI; -import java.net.URL; -import java.util.Comparator; -import java.util.Enumeration; import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.osgi.util.BundleFileLocatorHelperFactory; import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.osgi.framework.Bundle; -import org.osgi.framework.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,7 +83,6 @@ protected Bundle getBundle(Resource resource) public void parse(Set handlers, Bundle bundle) throws Exception { - Resource bundleResource = _bundleToResource.get(bundle); if (bundleResource == null) return; @@ -99,7 +90,6 @@ public void parse(Set handlers, Bundle bundle) if (!_parsed.add(_bundleToUri.get(bundle))) return; - parse(handlers, bundleResource); } @@ -109,25 +99,31 @@ public void parse(final Set handlers, Resource r) throws Exce if (r == null) return; - if (FileID.isJavaArchive(r.getPath())) - { - parseJar(handlers, r); + if (!r.exists()) return; - } if (r.isDirectory()) { parseDir(handlers, r); return; } - - if (FileID.isClassFile(r.getPath())) + else { - parseClass(handlers, null, r.getPath()); + if (FileID.isJavaArchive(r.getFileName())) + { + parseJar(handlers, r); + return; + } + + if (FileID.isClassFile(r.getFileName())) + { + parseClass(handlers, null, r); + return; + } } - //Not already parsed, it could be a file that actually is compressed but does not have - //.jar/.zip etc extension, such as equinox urls, so try to parse it + // Not already parsed, it could be a file that actually is compressed but does not have + // .jar/.zip etc extension, such as equinox urls, so try to parse it try { parseJar(handlers, r);