diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java index f4c094678c879..ff9054e17f5c5 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ArchivePathTree.java @@ -224,15 +224,30 @@ public boolean equals(Object obj) { && manifestEnabled == other.manifestEnabled; } - protected class OpenArchivePathTree extends DirectoryPathTree { + protected class OpenArchivePathTree extends OpenContainerPathTree { - // we don't make the field final as we want to nullify it on close - private volatile FileSystem fs; private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + // we don't make these fields final as we want to nullify them on close + private FileSystem fs; + private Path rootPath; + + private volatile boolean open = true; + protected OpenArchivePathTree(FileSystem fs) { - super(fs.getPath("/"), pathFilter, ArchivePathTree.this); + super(ArchivePathTree.this.pathFilter, ArchivePathTree.this); this.fs = fs; + this.rootPath = fs.getPath("/"); + } + + @Override + protected Path getContainerPath() { + return ArchivePathTree.this.archive; + } + + @Override + protected Path getRootPath() { + return rootPath; } protected ReentrantReadWriteLock.ReadLock readLock() { @@ -272,7 +287,7 @@ protected void initMultiReleaseMapping(Map mrMapping) { public boolean isOpen() { lock.readLock().lock(); try { - return fs != null && fs.isOpen(); + return open; } finally { lock.readLock().unlock(); } @@ -349,7 +364,7 @@ public Path getPath(String relativePath) { */ private void ensureOpen() { // let's not use isOpen() as ensureOpen() is always used inside a read lock - if (fs != null && fs.isOpen()) { + if (open) { return; } throw new RuntimeException("Failed to access " + ArchivePathTree.this.getRoots() @@ -358,28 +373,19 @@ private void ensureOpen() { @Override public void close() throws IOException { - Throwable t = null; lock.writeLock().lock(); try { - super.close(); - } catch (Throwable e) { - t = e; + open = false; + rootPath = null; + fs.close(); + } catch (IOException e) { throw e; } finally { - try { - fs.close(); - } catch (IOException e) { - if (t != null) { - e.addSuppressed(t); - } - throw e; - } finally { - // even when we close the fs, everything is kept as is in the fs instance - // and typically the cen, which is quite large - // let's make sure the fs is nullified for it to be garbage collected - fs = null; - lock.writeLock().unlock(); - } + // even when we close the fs, everything is kept as is in the fs instance + // and typically the cen, which is quite large + // let's make sure the fs is nullified for it to be garbage collected + fs = null; + lock.writeLock().unlock(); } } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java index 14ceb795ca6a1..b73d8acc1613d 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/DirectoryPathTree.java @@ -2,55 +2,14 @@ import java.io.IOException; import java.io.Serializable; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.regex.Pattern; -public class DirectoryPathTree extends PathTreeWithManifest implements OpenPathTree, Serializable { +public class DirectoryPathTree extends OpenContainerPathTree implements Serializable { private static final long serialVersionUID = 2255956884896445059L; - private static final boolean USE_WINDOWS_ABSOLUTE_PATH_PATTERN = !FileSystems.getDefault().getSeparator().equals("/"); - - private static volatile Pattern windowsAbsolutePathPattern; - - private static Pattern windowsAbsolutePathPattern() { - return windowsAbsolutePathPattern == null ? windowsAbsolutePathPattern = Pattern.compile("[a-zA-Z]:\\\\.*") - : windowsAbsolutePathPattern; - } - - static boolean isAbsolutePath(String path) { - return path != null && !path.isEmpty() - && (path.charAt(0) == '/' // we want to check for '/' on every OS - || USE_WINDOWS_ABSOLUTE_PATH_PATTERN - && (windowsAbsolutePathPattern().matcher(path).matches()) - || path.startsWith(FileSystems.getDefault().getSeparator())); - } - - static void ensureResourcePath(FileSystem fs, String path) { - if (isAbsolutePath(path)) { - throw new IllegalArgumentException("Expected a path relative to the root of the path tree but got " + path); - } - // this is to disallow reading outside the path tree root - if (path != null && path.contains("..")) { - for (Path pathElement : fs.getPath(path)) { - if (pathElement.toString().equals("..")) { - throw new IllegalArgumentException("'..' cannot be used in resource paths, but got " + path); - } - } - } - } - private Path dir; - private PathFilter pathFilter; /** * For deserialization @@ -68,90 +27,37 @@ public DirectoryPathTree(Path dir, PathFilter pathFilter) { } public DirectoryPathTree(Path dir, PathFilter pathFilter, boolean manifestEnabled) { - super(manifestEnabled); + super(pathFilter, manifestEnabled); this.dir = dir; - this.pathFilter = pathFilter; } protected DirectoryPathTree(Path dir, PathFilter pathFilter, PathTreeWithManifest pathTreeWithManifest) { - super(pathTreeWithManifest); + super(pathFilter, pathTreeWithManifest); this.dir = dir; - this.pathFilter = pathFilter; } @Override - public Collection getRoots() { - return List.of(dir); + protected Path getRootPath() { + return dir; } @Override - public void walk(PathVisitor visitor) { - PathTreeVisit.walk(dir, dir, dir, pathFilter, getMultiReleaseMapping(), visitor); + protected Path getContainerPath() { + return dir; } @Override - public void walkIfContains(String relativePath, PathVisitor visitor) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return; - } - final Path walkDir = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - if (!Files.exists(walkDir)) { - return; - } - PathTreeVisit.walk(dir, dir, walkDir, pathFilter, getMultiReleaseMapping(), visitor); - } - - private void ensureResourcePath(String path) { - ensureResourcePath(dir.getFileSystem(), path); - } - - @Override - protected T apply(String relativePath, Function func, boolean manifestEnabled) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return func.apply(null); - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - if (!Files.exists(path)) { - return func.apply(null); - } - return PathTreeVisit.process(dir, dir, path, pathFilter, func); - } - - @Override - public void accept(String relativePath, Consumer consumer) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - consumer.accept(null); - return; - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - if (!Files.exists(path)) { - consumer.accept(null); - return; - } - PathTreeVisit.consume(dir, dir, path, pathFilter, consumer); + public boolean isOpen() { + return true; } @Override - public boolean contains(String relativePath) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return false; - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - return Files.exists(path); + public void close() throws IOException { } @Override - public Path getPath(String relativePath) { - ensureResourcePath(relativePath); - if (!PathFilter.isVisible(pathFilter, relativePath)) { - return null; - } - final Path path = dir.resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); - return Files.exists(path) ? path : null; + public PathTree getOriginalTree() { + return this; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { @@ -165,41 +71,4 @@ private void readObject(java.io.ObjectInputStream in) throws IOException, ClassN pathFilter = (PathFilter) in.readObject(); manifestEnabled = in.readBoolean(); } - - @Override - public OpenPathTree open() { - return this; - } - - @Override - public boolean isOpen() { - return true; - } - - @Override - public void close() throws IOException { - } - - @Override - public PathTree getOriginalTree() { - return this; - } - - @Override - public int hashCode() { - return Objects.hash(dir, pathFilter, manifestEnabled); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DirectoryPathTree other = (DirectoryPathTree) obj; - return Objects.equals(dir, other.dir) && Objects.equals(pathFilter, other.pathFilter) - && manifestEnabled == other.manifestEnabled; - } } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java index d1efedc41dad6..4f31e89c6189d 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/EmptyPathTree.java @@ -6,7 +6,6 @@ import java.util.Collections; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public class EmptyPathTree implements OpenPathTree { @@ -22,7 +21,7 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { return null; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java index 348b887742a27..cb80e315b5976 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilePathTree.java @@ -7,7 +7,6 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; class FilePathTree implements OpenPathTree { @@ -29,7 +28,7 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { return null; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java index 47b03c66d8031..25de79a0eba9a 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/FilteredPathTree.java @@ -6,7 +6,6 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public class FilteredPathTree implements PathTree { @@ -24,8 +23,8 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { - return original.getManifest(); + public ManifestAttributes getManifestAttributes() { + return original.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java new file mode 100644 index 0000000000000..34716ebb6b2e6 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/ManifestAttributes.java @@ -0,0 +1,72 @@ +package io.quarkus.paths; + +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Manifests for some libraries can be quite large (e.g. for commons-codec, it is 21 KB). + *

+ * Given we keep a lot of ArchivePathTree around, it seems like a good idea to only + * keep around the Manifest entries that we actually use in Quarkus. + *

+ * This can be extended further in the future if we need more attributes. + */ +public class ManifestAttributes { + + private String specificationTitle; + private String specificationVersion; + private String specificationVendor; + + private String implementationTitle; + private String implementationVersion; + private String implementationVendor; + + private boolean multiRelease; + + public static ManifestAttributes of(Manifest manifest) { + if (manifest == null) { + return null; + } + + return new ManifestAttributes(manifest); + } + + private ManifestAttributes(Manifest manifest) { + specificationTitle = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_TITLE); + specificationVersion = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VERSION); + specificationVendor = manifest.getMainAttributes().getValue(Attributes.Name.SPECIFICATION_VENDOR); + implementationTitle = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_TITLE); + implementationVersion = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); + implementationVendor = manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VENDOR); + + multiRelease = Boolean.parseBoolean(manifest.getMainAttributes().getValue(Attributes.Name.MULTI_RELEASE)); + } + + public String getSpecificationTitle() { + return specificationTitle; + } + + public String getSpecificationVersion() { + return specificationVersion; + } + + public String getSpecificationVendor() { + return specificationVendor; + } + + public String getImplementationTitle() { + return implementationTitle; + } + + public String getImplementationVersion() { + return implementationVersion; + } + + public String getImplementationVendor() { + return implementationVendor; + } + + public boolean isMultiRelease() { + return multiRelease; + } +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java index 9919662363250..f94b0bc9cc4f3 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/MultiRootPathTree.java @@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public class MultiRootPathTree implements OpenPathTree { @@ -32,9 +31,9 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { for (PathTree tree : trees) { - final Manifest m = tree.getManifest(); + final ManifestAttributes m = tree.getManifestAttributes(); if (m != null) { return m; } diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java new file mode 100644 index 0000000000000..ecdf1ba10e483 --- /dev/null +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/OpenContainerPathTree.java @@ -0,0 +1,189 @@ +package io.quarkus.paths; + +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.regex.Pattern; + +public abstract class OpenContainerPathTree extends PathTreeWithManifest implements OpenPathTree { + + private static final boolean USE_WINDOWS_ABSOLUTE_PATH_PATTERN = !FileSystems.getDefault().getSeparator().equals("/"); + + private static volatile Pattern windowsAbsolutePathPattern; + + private static Pattern windowsAbsolutePathPattern() { + return windowsAbsolutePathPattern == null ? windowsAbsolutePathPattern = Pattern.compile("[a-zA-Z]:\\\\.*") + : windowsAbsolutePathPattern; + } + + static boolean isAbsolutePath(String path) { + return path != null && !path.isEmpty() + && (path.charAt(0) == '/' // we want to check for '/' on every OS + || USE_WINDOWS_ABSOLUTE_PATH_PATTERN + && (windowsAbsolutePathPattern().matcher(path).matches()) + || path.startsWith(FileSystems.getDefault().getSeparator())); + } + + static void ensureResourcePath(FileSystem fs, String path) { + if (isAbsolutePath(path)) { + throw new IllegalArgumentException("Expected a path relative to the root of the path tree but got " + path); + } + // this is to disallow reading outside the path tree root + if (path != null && path.contains("..")) { + for (Path pathElement : fs.getPath(path)) { + if (pathElement.toString().equals("..")) { + throw new IllegalArgumentException("'..' cannot be used in resource paths, but got " + path); + } + } + } + } + + protected PathFilter pathFilter; + + /** + * For deserialization of DirectoryPathTree + */ + public OpenContainerPathTree() { + } + + protected OpenContainerPathTree(PathFilter pathFilter) { + this(pathFilter, false); + } + + protected OpenContainerPathTree(PathFilter pathFilter, boolean manifestEnabled) { + super(manifestEnabled); + this.pathFilter = pathFilter; + } + + protected OpenContainerPathTree(PathFilter pathFilter, PathTreeWithManifest pathTreeWithManifest) { + super(pathTreeWithManifest); + this.pathFilter = pathFilter; + } + + /** + * This is the path to the container. + *

+ * In the case of a zip archive, it's the path of the archive. + * In the case of a directory, it's the directory. + *

+ * Should only be used for equals/hashCode. + */ + protected abstract Path getContainerPath(); + + /** + * This is the path to the container. + *

+ * In the case of a zip archive, it's the path to the root of the archive (i.e. a ZipPath). + * In the case of a directory, it's the directory. + *

+ * Should be used for any read operation on the container. + */ + protected abstract Path getRootPath(); + + @Override + public OpenPathTree open() { + return this; + } + + @Override + public Collection getRoots() { + return List.of(getContainerPath()); + } + + @Override + public void walk(PathVisitor visitor) { + PathTreeVisit.walk(getRootPath(), getRootPath(), getRootPath(), pathFilter, getMultiReleaseMapping(), + visitor); + } + + @Override + public void walkIfContains(String relativePath, PathVisitor visitor) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return; + } + final Path walkDir = getRootPath() + .resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + if (!Files.exists(walkDir)) { + return; + } + PathTreeVisit.walk(getRootPath(), getRootPath(), walkDir, pathFilter, getMultiReleaseMapping(), visitor); + } + + private void ensureResourcePath(String path) { + ensureResourcePath(getRootPath().getFileSystem(), path); + } + + @Override + protected T apply(String relativePath, Function func, boolean manifestEnabled) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return func.apply(null); + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + if (!Files.exists(path)) { + return func.apply(null); + } + return PathTreeVisit.process(getRootPath(), getRootPath(), path, pathFilter, func); + } + + @Override + public void accept(String relativePath, Consumer consumer) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + consumer.accept(null); + return; + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + if (!Files.exists(path)) { + consumer.accept(null); + return; + } + PathTreeVisit.consume(getRootPath(), getRootPath(), path, pathFilter, consumer); + } + + @Override + public boolean contains(String relativePath) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return false; + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + return Files.exists(path); + } + + @Override + public Path getPath(String relativePath) { + ensureResourcePath(relativePath); + if (!PathFilter.isVisible(pathFilter, relativePath)) { + return null; + } + final Path path = getRootPath().resolve(manifestEnabled ? toMultiReleaseRelativePath(relativePath) : relativePath); + return Files.exists(path) ? path : null; + } + + @Override + public int hashCode() { + return Objects.hash(getContainerPath(), pathFilter, manifestEnabled); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OpenContainerPathTree other = (OpenContainerPathTree) obj; + return Objects.equals(getContainerPath(), other.getContainerPath()) + && Objects.equals(pathFilter, other.pathFilter) + && manifestEnabled == other.manifestEnabled; + } +} diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java index 92c288e10db04..f8e4280b4f463 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTree.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; public interface PathTree { @@ -83,6 +82,9 @@ static PathTree ofArchive(Path archive, PathFilter filter) { /** * The roots of the path tree. + *

+ * Note that for archives, it will return the path to the archive itself, + * not a path that you can browse. * * @return roots of the path tree */ @@ -98,12 +100,12 @@ default boolean isEmpty() { } /** - * If {@code META-INF/MANIFEST.MF} found, reads it and returns an instance of {@link java.util.jar.Manifest}, - * otherwise returns null. + * If {@code META-INF/MANIFEST.MF} found, reads it and returns an instance of {@link ManifestAttributes}, + * a trimmed down version of the Manifest, otherwise returns null. * * @return parsed {@code META-INF/MANIFEST.MF} if it's found, otherwise {@code null} */ - Manifest getManifest(); + ManifestAttributes getManifestAttributes(); /** * Walks the tree. diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java index 8576f4303a30c..3b3608416f2d2 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/PathTreeWithManifest.java @@ -42,7 +42,7 @@ public abstract class PathTreeWithManifest implements PathTree { protected boolean manifestEnabled; private final ReentrantReadWriteLock manifestInfoLock = new ReentrantReadWriteLock(); - private transient Manifest manifest; + private transient ManifestAttributes manifestAttributes; protected transient boolean manifestInitialized; protected volatile Map multiReleaseMapping; @@ -61,7 +61,7 @@ protected PathTreeWithManifest(boolean manifestEnabled) { protected PathTreeWithManifest(PathTreeWithManifest pathTreeWithManifest) { pathTreeWithManifest.manifestReadLock().lock(); try { - this.manifest = pathTreeWithManifest.manifest; + this.manifestAttributes = pathTreeWithManifest.manifestAttributes; this.manifestInitialized = pathTreeWithManifest.manifestInitialized; this.multiReleaseMapping = pathTreeWithManifest.multiReleaseMapping; } finally { @@ -78,12 +78,12 @@ public T apply(String relativePath, Function func) { protected abstract T apply(String relativePath, Function func, boolean manifestEnabled); @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { // Optimistically try with a lock that allows concurrent access first, for performance. manifestReadLock().lock(); try { if (manifestInitialized) { - return manifest; + return manifestAttributes; } } finally { manifestReadLock().unlock(); @@ -94,18 +94,18 @@ public Manifest getManifest() { if (manifestInitialized) { // Someone else got here between our call to manifestReadLock().unlock() // and our call to manifestWriteLock().lock(); it can happen. - return manifest; + return manifestAttributes; } final Manifest m = apply("META-INF/MANIFEST.MF", ManifestReader.INSTANCE, false); initManifest(m); } finally { manifestWriteLock().unlock(); } - return manifest; + return manifestAttributes; } protected void initManifest(Manifest m) { - manifest = m; + manifestAttributes = ManifestAttributes.of(m); manifestInitialized = true; } @@ -118,7 +118,9 @@ protected ReadLock manifestReadLock() { } public boolean isMultiReleaseJar() { - return isMultiReleaseJar(getManifest()); + ManifestAttributes manifestAttributes = getManifestAttributes(); + + return manifestAttributes != null && manifestAttributes.isMultiRelease(); } protected Map getMultiReleaseMapping() { @@ -144,10 +146,6 @@ protected String toMultiReleaseRelativePath(String relativePath) { return getMultiReleaseMapping().getOrDefault(relativePath, relativePath); } - private static boolean isMultiReleaseJar(final Manifest m) { - return m != null && Boolean.parseBoolean(m.getMainAttributes().getValue("Multi-Release")); - } - private static class MultiReleaseMappingReader implements Function> { private static final MultiReleaseMappingReader INSTANCE = new MultiReleaseMappingReader(); diff --git a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java index a8cc3f4b41962..0ea0048549c8b 100644 --- a/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java +++ b/independent-projects/bootstrap/app-model/src/main/java/io/quarkus/paths/SharedArchivePathTree.java @@ -10,7 +10,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; /** * While {@link ArchivePathTree} implementation is thread-safe, this implementation @@ -58,7 +57,7 @@ public OpenPathTree open() { return new CallerOpenPathTree(lastOpen); } try { - this.lastOpen = new SharedOpenArchivePathTree(openFs()); + this.lastOpen = new SharedOpenArchivePathTree(archive, openFs()); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -69,7 +68,7 @@ private class SharedOpenArchivePathTree extends OpenArchivePathTree { private final AtomicInteger users = new AtomicInteger(1); - protected SharedOpenArchivePathTree(FileSystem fs) { + protected SharedOpenArchivePathTree(Path archivePath, FileSystem fs) { super(fs); openCount.incrementAndGet(); } @@ -144,8 +143,8 @@ public Collection getRoots() { } @Override - public Manifest getManifest() { - return delegate.getManifest(); + public ManifestAttributes getManifestAttributes() { + return delegate.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java index 7b66d6c856413..89d54ad91fb0c 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/app/CuratedApplication.java @@ -15,7 +15,6 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; -import java.util.jar.Manifest; import java.util.stream.Collectors; import io.quarkus.bootstrap.classloading.ClassPathElement; @@ -27,6 +26,7 @@ import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.DependencyFlags; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; import io.quarkus.paths.PathTree; @@ -499,8 +499,8 @@ public ProtectionDomain getProtectionDomain() { } @Override - public Manifest getManifest() { - return delegate.getManifest(); + public ManifestAttributes getManifestAttributes() { + return delegate.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java index d2a2faed0a835..be85f0786cfd8 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/AbstractClassPathElement.java @@ -2,37 +2,40 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.jar.Manifest; import org.jboss.logging.Logger; +import io.quarkus.paths.ManifestAttributes; + public abstract class AbstractClassPathElement implements ClassPathElement { private static final Logger log = Logger.getLogger(AbstractClassPathElement.class); - private volatile Manifest manifest; + private volatile ManifestAttributes manifestAttributes; private volatile boolean manifestInitialized = false; @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { if (manifestInitialized) { - return manifest; + return manifestAttributes; } synchronized (this) { if (manifestInitialized) { - return manifest; + return manifestAttributes; } - manifest = readManifest(); + manifestAttributes = readManifest(); manifestInitialized = true; } - return manifest; + return manifestAttributes; } - protected Manifest readManifest() { + protected ManifestAttributes readManifest() { final ClassPathResource mf = getResource("META-INF/MANIFEST.MF"); if (mf != null) { - try { - return new Manifest(new ByteArrayInputStream(mf.getData())); + try (InputStream manifestIs = new ByteArrayInputStream(mf.getData())) { + return ManifestAttributes.of(new Manifest(manifestIs)); } catch (IOException e) { log.warnf("Failed to parse manifest for %s", toString(), e); } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java index 0e319d437e7b7..96c6689a4816d 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/ClassPathElement.java @@ -7,11 +7,11 @@ import java.util.List; import java.util.Set; import java.util.function.Function; -import java.util.jar.Manifest; import io.quarkus.maven.dependency.ArtifactKey; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.paths.EmptyPathTree; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; import io.quarkus.paths.PathTree; @@ -80,7 +80,7 @@ default ResolvedDependency getResolvedDependency() { */ ProtectionDomain getProtectionDomain(); - Manifest getManifest(); + ManifestAttributes getManifestAttributes(); /** * Checks whether this is a runtime classpath element @@ -133,7 +133,7 @@ public ProtectionDomain getProtectionDomain() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { return null; } diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java index 3e6f9a897d277..a8dbbd2d06ec7 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/FilteredClassPathElement.java @@ -7,9 +7,9 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Function; -import java.util.jar.Manifest; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; public class FilteredClassPathElement implements ClassPathElement { @@ -63,9 +63,9 @@ public ProtectionDomain getProtectionDomain() { } @Override - public Manifest getManifest() { + public ManifestAttributes getManifestAttributes() { //we don't support filtering the manifest - return delegate.getManifest(); + return delegate.getManifestAttributes(); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java index a1da4f0e74114..8126c264dbb43 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/PathTreeClassPathElement.java @@ -21,11 +21,11 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Function; -import java.util.jar.Manifest; import org.jboss.logging.Logger; import io.quarkus.maven.dependency.ResolvedDependency; +import io.quarkus.paths.ManifestAttributes; import io.quarkus.paths.OpenPathTree; import io.quarkus.paths.PathTree; import io.quarkus.paths.PathVisit; @@ -176,8 +176,8 @@ public void visitPath(PathVisit visit) { } @Override - protected Manifest readManifest() { - return apply(OpenPathTree::getManifest); + protected ManifestAttributes readManifest() { + return apply(OpenPathTree::getManifestAttributes); } @Override diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java index 93b862d27166b..e48f65016950c 100644 --- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java +++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java @@ -26,11 +26,11 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.jar.Attributes; -import java.util.jar.Manifest; import org.jboss.logging.Logger; +import io.quarkus.paths.ManifestAttributes; + /** * The ClassLoader used for non production Quarkus applications (i.e. dev and test mode). */ @@ -550,15 +550,14 @@ private void definePackage(String name, ClassPathElement classPathElement) { if ((pkgName != null) && definedPackages.get(pkgName) == null) { synchronized (getClassLoadingLock(pkgName)) { if (definedPackages.get(pkgName) == null) { - Manifest mf = classPathElement.getManifest(); - if (mf != null) { - Attributes ma = mf.getMainAttributes(); - definedPackages.put(pkgName, definePackage(pkgName, ma.getValue(Attributes.Name.SPECIFICATION_TITLE), - ma.getValue(Attributes.Name.SPECIFICATION_VERSION), - ma.getValue(Attributes.Name.SPECIFICATION_VENDOR), - ma.getValue(Attributes.Name.IMPLEMENTATION_TITLE), - ma.getValue(Attributes.Name.IMPLEMENTATION_VERSION), - ma.getValue(Attributes.Name.IMPLEMENTATION_VENDOR), null)); + ManifestAttributes manifest = classPathElement.getManifestAttributes(); + if (manifest != null) { + definedPackages.put(pkgName, definePackage(pkgName, manifest.getSpecificationTitle(), + manifest.getSpecificationVersion(), + manifest.getSpecificationVendor(), + manifest.getImplementationTitle(), + manifest.getImplementationVersion(), + manifest.getImplementationVendor(), null)); return; }