Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package plexus-io for openSUSE:Factory checked in at 2026-09-29 17:49:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/plexus-io (Old) and /work/SRC/openSUSE:Factory/.plexus-io.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "plexus-io" Tue Sep 29 17:49:27 2026 rev:9 rq:1381210 version:3.8.0 Changes: -------- --- /work/SRC/openSUSE:Factory/plexus-io/plexus-io.changes 2026-03-01 22:15:02.050479578 +0100 +++ /work/SRC/openSUSE:Factory/.plexus-io.new.383539/plexus-io.changes 2026-09-29 17:50:47.092725314 +0200 @@ -1,0 +2,37 @@ +Mon Sep 28 11:38:49 UTC 2026 - Fridrich Strba <[email protected]> + +- Upgrade to version 3.8.0 + * New features and improvements + + Expose hard-link identities for untransformed resources + * Documentation updates + + Drop the Source Xref menu item + + Stop pinning a version in the README snippet + * Maintenance + + Drop the Source Xref menu item + * Build + + Point the SCM URLs at HTTPS + + Draft the version the labels imply, not always a patch +- Changes of version 3.7.0 + * New features and improvements + + Establish an automatic module name + * Bug Fixes + + Follow symbolic links to files when isFollowingSymLinks() is + set + * Documentation updates + + Describe the artifact in the POM + + Rewrite README with usage, status and version guidance + + Restore the document titles dropped when the pages were ported + * Maintenance + + Port the site documentation from APT to Markdown + + JUnit Jupiter best practices + * Build + + pdate parent to plexus 27 + * Dependency updates + + Bump org.eclipse.sisu:org.eclipse.sisu.inject from 0.9.0.M4 to + 1.1.0 + + Bump commons-io:commons-io from 2.20.0 to 2.22.0 + + Bump org.codehaus.plexus:plexus-utils from 3.6.0 to 3.6.2 + + Bump org.codehaus.plexus:plexus from 24 to 25 + + Bump org.codehaus.plexus:plexus-testing from 2.0.1 to 2.1.0 + +------------------------------------------------------------------- Old: ---- plexus-io-3.6.0.tar.gz New: ---- plexus-io-3.8.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ plexus-io.spec ++++++ --- /var/tmp/diff_new_pack.bgxhDJ/_old 2026-09-29 17:50:48.154769662 +0200 +++ /var/tmp/diff_new_pack.bgxhDJ/_new 2026-09-29 17:50:48.157769788 +0200 @@ -17,7 +17,7 @@ Name: plexus-io -Version: 3.6.0 +Version: 3.8.0 Release: 0 Summary: Plexus IO Components License: Apache-2.0 ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.bgxhDJ/_old 2026-09-29 17:50:48.231772878 +0200 +++ /var/tmp/diff_new_pack.bgxhDJ/_new 2026-09-29 17:50:48.234773003 +0200 @@ -1,6 +1,6 @@ -mtime: 1772203931 -commit: 52d5319298584a50506354df15e2700d1c6c3a73133690e4a1d0e4e9f9ede258 -url: https://src.opensuse.org/java-packages/plexus-io.git -revision: 52d5319298584a50506354df15e2700d1c6c3a73133690e4a1d0e4e9f9ede258 +mtime: 1790596255 +commit: 400d8ac4eb814bfc6d81935c8fc72faf2583184b8f3ff661d89de9b464e30c53 +url: https://src.opensuse.org/java-packages/plexus-io +revision: 400d8ac4eb814bfc6d81935c8fc72faf2583184b8f3ff661d89de9b464e30c53 projectscmsync: https://src.opensuse.org/java-packages/_ObsPrj ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-09-28 13:50:55.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ plexus-io-3.6.0.tar.gz -> plexus-io-3.8.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/.github/release-drafter.yml new/plexus-io-plexus-io-3.8.0/.github/release-drafter.yml --- old/plexus-io-plexus-io-3.6.0/.github/release-drafter.yml 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/.github/release-drafter.yml 2026-09-19 02:25:52.000000000 +0200 @@ -1,3 +1,8 @@ _extends: .github -tag-template: plexus-io-$NEXT_MINOR_VERSION -name-template: plexus-io-$NEXT_MINOR_VERSION + +# tag-prefix is what lets the drafter parse the version out of the last tag. +# Without it the drafter reads what it can from the tag string, and plexus-i18n +# once drafted 18.0.1 by finding the 18 in its own name. +tag-prefix: plexus-io- +tag-template: plexus-io-$RESOLVED_VERSION +name-template: plexus-io-$RESOLVED_VERSION diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/README.md new/plexus-io-plexus-io-3.8.0/README.md --- old/plexus-io-plexus-io-3.6.0/README.md 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/README.md 2026-09-19 02:25:52.000000000 +0200 @@ -1,16 +1,45 @@ -Plexus-IO -========= +# Plexus IO -[](https://github.com/codehaus-plexus/plexus-io/actions/workflows/maven.yml) -[](https://search.maven.org/artifact/org.codehaus.plexus/plexus-io) +[](https://central.sonatype.com/artifact/org.codehaus.plexus/plexus-io) +[](https://github.com/codehaus-plexus/plexus-io/actions/workflows/maven.yml) [](https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/org/codehaus/plexus/plexus-io/README.md) -The current master is now at https://github.com/codehaus-plexus/plexus-io +Abstractions over files and resources: a uniform view of something to be read whether it is on disk, in an +archive or in memory, plus the selectors and mappers used to filter and rename those resources. -For publishing [the site](https://codehaus-plexus.github.io/plexus-io/) do the following: +Most people meet it through [`plexus-archiver`](https://github.com/codehaus-plexus/plexus-archiver), which +uses it to decide what goes into an archive and under what name. +## Status + +Maintained, conservatively. The API is stable and widely depended on transitively, so changes are mostly +bug fixes and dependency updates. + +## Using it + +```xml +<dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-io</artifactId> +</dependency> ``` -mvn -Preporting verify site-deploy -``` -Enjoy! +Check the badge above for the current version. + +## Requirements + +Java 8 or later. + +## Documentation + +- [Project site](https://codehaus-plexus.github.io/plexus-io/) — including [file mappers](https://codehaus-plexus.github.io/plexus-io/filemappers.html) and [file selectors](https://codehaus-plexus.github.io/plexus-io/fileselectors.html) +- [Javadoc](https://javadoc.io/doc/org.codehaus.plexus/plexus-io) +- [Release notes](https://github.com/codehaus-plexus/plexus-io/releases) + +## Contributing + +See [CONTRIBUTING.md](https://github.com/codehaus-plexus/.github/blob/master/CONTRIBUTING.md). In short: +`mvn verify` builds, and run `mvn spotless:apply` before pushing or CI will fail on formatting. + +Please report security vulnerabilities privately — see +[SECURITY.md](https://github.com/codehaus-plexus/.github/blob/master/SECURITY.md), not a public issue. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/pom.xml new/plexus-io-plexus-io-3.8.0/pom.xml --- old/plexus-io-plexus-io-3.6.0/pom.xml 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/pom.xml 2026-09-19 02:25:52.000000000 +0200 @@ -5,18 +5,20 @@ <parent> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus</artifactId> - <version>24</version> + <version>27</version> </parent> <artifactId>plexus-io</artifactId> - <version>3.6.0</version> + <version>3.8.0</version> <name>Plexus IO Components</name> + <description>A component providing a uniform view of file, archive and in-memory resources, with selectors and + mappers to filter and rename them.</description> <scm> - <connection>scm:git:[email protected]:codehaus-plexus/plexus-io.git</connection> - <developerConnection>scm:git:[email protected]:codehaus-plexus/plexus-io.git</developerConnection> - <tag>plexus-io-3.6.0</tag> + <connection>scm:git:https://github.com/codehaus-plexus/plexus-io.git</connection> + <developerConnection>${project.scm.connection}</developerConnection> + <tag>plexus-io-3.8.0</tag> <url>http://github.com/codehaus-plexus/plexus-io</url> </scm> <issueManagement> @@ -32,7 +34,7 @@ <properties> <slf4jVersion>1.7.36</slf4jVersion> - <project.build.outputTimestamp>2025-11-07T18:10:00Z</project.build.outputTimestamp> + <project.build.outputTimestamp>2026-09-19T00:25:48Z</project.build.outputTimestamp> </properties> <dependencies> @@ -45,7 +47,7 @@ <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-utils</artifactId> - <version>3.6.0</version> + <version>3.6.2</version> </dependency> <dependency> <groupId>com.google.code.findbugs</groupId> @@ -56,7 +58,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>2.20.0</version> + <version>2.22.0</version> </dependency> <!-- Tests --> @@ -74,14 +76,14 @@ <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-testing</artifactId> - <version>2.0.1</version> + <version>2.2.0</version> <scope>test</scope> </dependency> <!-- Sisu and dependencies --> <dependency> <groupId>org.eclipse.sisu</groupId> <artifactId>org.eclipse.sisu.inject</artifactId> - <version>0.9.0.M4</version> + <version>1.1.0</version> <scope>test</scope> </dependency> </dependencies> @@ -89,6 +91,17 @@ <build> <plugins> <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestEntries> + <Automatic-Module-Name>org.codehaus.plexus.components.io</Automatic-Module-Name> + </manifestEntries> + </archive> + </configuration> + </plugin> + <plugin> <groupId>org.eclipse.sisu</groupId> <artifactId>sisu-maven-plugin</artifactId> </plugin> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/functions/HardLinkIdentitySupplier.java new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/functions/HardLinkIdentitySupplier.java --- old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/functions/HardLinkIdentitySupplier.java 1970-01-01 01:00:00.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/functions/HardLinkIdentitySupplier.java 2026-09-19 02:25:52.000000000 +0200 @@ -0,0 +1,39 @@ +package org.codehaus.plexus.components.io.functions; + +/* + * Copyright 2026 The plexus developers. + * + * 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. + */ + +import java.io.IOException; + +/** + * Optional identity for resources whose contents can share one hard-link payload. + * Implementations must return null when transformation or a replacement content + * supplier invalidates that relationship. Equal identities must denote the same + * content within a stable source, not merely equal bytes from unrelated files. + * Consumers must also compare the metadata they intend to write. + * + * @since 3.7.1 + */ +public interface HardLinkIdentitySupplier { + /** + * Returns a source-scoped identity suitable for equality comparisons. + * Identities must only be cached for the duration of one archive operation. + * + * @return the content identity, or null when preservation cannot be guaranteed + * @throws IOException if source attributes cannot be read + */ + Object getHardLinkIdentity() throws IOException; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/Deferred.java new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/Deferred.java --- old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/Deferred.java 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/Deferred.java 2026-09-19 02:25:52.000000000 +0200 @@ -25,11 +25,12 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.DeferredFileOutputStream; import org.codehaus.plexus.components.io.functions.ContentSupplier; +import org.codehaus.plexus.components.io.functions.HardLinkIdentitySupplier; import org.codehaus.plexus.components.io.functions.NameSupplier; import org.codehaus.plexus.components.io.functions.SizeSupplier; import org.codehaus.plexus.components.io.resources.proxy.ProxyFactory; -class Deferred implements ContentSupplier, NameSupplier, SizeSupplier { +class Deferred implements ContentSupplier, NameSupplier, SizeSupplier, HardLinkIdentitySupplier { final DeferredFileOutputStream dfos; final PlexusIoResource resource; @@ -87,6 +88,19 @@ return owner.getName(resource); } + /** + * Preserves source identity only when this wrapper has not transformed the contents. + * + * @return the source identity, or null after transformation + * @throws IOException if the source identity cannot be read + */ + @Override + public Object getHardLinkIdentity() throws IOException { + return dfos == null && resource instanceof HardLinkIdentitySupplier + ? ((HardLinkIdentitySupplier) resource).getHardLinkIdentity() + : null; + } + public PlexusIoResource asResource() { return ProxyFactory.createProxy(resource, Deferred.this); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResource.java new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResource.java --- old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResource.java 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResource.java 2026-09-19 02:25:52.000000000 +0200 @@ -25,7 +25,10 @@ import java.io.InputStream; import java.net.URL; import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.util.Arrays; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.DeferredFileOutputStream; @@ -34,6 +37,7 @@ import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes; import org.codehaus.plexus.components.io.functions.ContentSupplier; import org.codehaus.plexus.components.io.functions.FileSupplier; +import org.codehaus.plexus.components.io.functions.HardLinkIdentitySupplier; import org.codehaus.plexus.components.io.functions.InputStreamTransformer; import org.codehaus.plexus.components.io.functions.ResourceAttributeSupplier; @@ -42,7 +46,8 @@ /** * Implementation of {@link PlexusIoResource} for files. */ -public class PlexusIoFileResource extends AbstractPlexusIoResource implements ResourceAttributeSupplier, FileSupplier { +public class PlexusIoFileResource extends AbstractPlexusIoResource + implements ResourceAttributeSupplier, FileSupplier, HardLinkIdentitySupplier { @Nonnull private final File file; @@ -55,6 +60,8 @@ private final ContentSupplier contentSupplier; + private final boolean originalContent; + private final DeferredFileOutputStream dfos; protected PlexusIoFileResource(@Nonnull File file, @Nonnull String name, @Nonnull PlexusIoResourceAttributes attrs) @@ -98,6 +105,8 @@ boolean hasTransformer = streamTransformer != null && streamTransformer != identityTransformer; InputStreamTransformer transToUse = streamTransformer != null ? streamTransformer : identityTransformer; + originalContent = contentSupplier == null && !hasTransformer; + dfos = hasTransformer && file.isFile() ? asDeferredStream(this.contentSupplier, transToUse, this) : null; } @@ -205,6 +214,28 @@ return getAttributes().isSymbolicLink(); } + /** + * Identifies an untransformed regular file without following symbolic links. + * Subclasses that change content must explicitly supply their own guarantee. + * + * @return a filesystem-scoped file identity, or null for unknown content + * @throws IOException if the file attributes cannot be read + */ + @Override + public Object getHardLinkIdentity() throws IOException { + // A backing file alone does not describe custom suppliers or transformed bytes. + if (!originalContent || getClass() != PlexusIoFileResource.class) { + return null; + } + BasicFileAttributes attrs = + Files.readAttributes(file.toPath(), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); + if (!attrs.isRegularFile() || attrs.fileKey() == null) { + return null; + } + // Include the observed content version so a changed source cannot reuse an old payload. + return Arrays.asList(file.toPath().getFileSystem(), attrs.fileKey(), attrs.size(), attrs.lastModifiedTime()); + } + protected DeferredFileOutputStream getDfos() { return dfos; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollection.java new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollection.java --- old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollection.java 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollection.java 2026-09-19 02:25:52.000000000 +0200 @@ -145,7 +145,7 @@ String sourceDir = name.replace('\\', '/'); File f = new File(dir, sourceDir); - FileAttributes fattrs = new FileAttributes(f); + FileAttributes fattrs = new FileAttributes(f, isFollowingSymLinks()); PlexusIoResourceAttributes attrs = mergeAttributes(fattrs, fattrs.isDirectory()); String remappedName = getName(name); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ProxyFactory.java new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ProxyFactory.java --- old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ProxyFactory.java 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ProxyFactory.java 2026-09-19 02:25:52.000000000 +0200 @@ -22,6 +22,7 @@ import java.util.List; import org.codehaus.plexus.components.io.functions.FileSupplier; +import org.codehaus.plexus.components.io.functions.HardLinkIdentitySupplier; import org.codehaus.plexus.components.io.functions.ResourceAttributeSupplier; import org.codehaus.plexus.components.io.functions.SymlinkDestinationSupplier; import org.codehaus.plexus.components.io.resources.PlexusIoResource; @@ -31,6 +32,9 @@ List<Class> interfaces = new ArrayList<>(); interfaces.add(PlexusIoResource.class); + if (target instanceof HardLinkIdentitySupplier || alternateSupplier instanceof HardLinkIdentitySupplier) { + interfaces.add(HardLinkIdentitySupplier.class); + } if (target instanceof SymlinkDestinationSupplier) interfaces.add(SymlinkDestinationSupplier.class); if (target instanceof FileSupplier) interfaces.add(FileSupplier.class); if (target instanceof ResourceAttributeSupplier) interfaces.add(ResourceAttributeSupplier.class); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ResourceInvocationHandler.java new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ResourceInvocationHandler.java --- old/plexus-io-plexus-io-3.6.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ResourceInvocationHandler.java 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/main/java/org/codehaus/plexus/components/io/resources/proxy/ResourceInvocationHandler.java 2026-09-19 02:25:52.000000000 +0200 @@ -21,6 +21,7 @@ import java.lang.reflect.Method; import org.codehaus.plexus.components.io.functions.ContentSupplier; +import org.codehaus.plexus.components.io.functions.HardLinkIdentitySupplier; import org.codehaus.plexus.components.io.functions.NameSupplier; import org.codehaus.plexus.components.io.functions.ResourceAttributeSupplier; import org.codehaus.plexus.components.io.functions.SizeSupplier; @@ -30,6 +31,8 @@ class ResourceInvocationHandler implements InvocationHandler { private final PlexusIoResource testImpl; + private final HardLinkIdentitySupplier hardLinkIdentitySupplier; + private final ContentSupplier contentSupplier; private final NameSupplier nameSupplier; private final SizeSupplier sizeSupplier; @@ -38,6 +41,7 @@ public ResourceInvocationHandler(@Nonnull PlexusIoResource target, Object alternativeHandler) { this.testImpl = target; + this.hardLinkIdentitySupplier = asOrNull(alternativeHandler, HardLinkIdentitySupplier.class); this.contentSupplier = asOrNull(alternativeHandler, ContentSupplier.class); this.nameSupplier = asOrNull(alternativeHandler, NameSupplier.class); this.sizeSupplier = asOrNull(alternativeHandler, SizeSupplier.class); @@ -53,6 +57,15 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); + if ("getHardLinkIdentity".equals(name)) { + // Replacing contents invalidates identity unless the wrapper explicitly preserves it. + if (hardLinkIdentitySupplier != null) { + return hardLinkIdentitySupplier.getHardLinkIdentity(); + } + return contentSupplier == null && sizeSupplier == null && testImpl instanceof HardLinkIdentitySupplier + ? ((HardLinkIdentitySupplier) testImpl).getHardLinkIdentity() + : null; + } if (contentSupplier != null && "getContents".equals(name)) { return contentSupplier.getContents(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/site/apt/filemappers.apt new/plexus-io-plexus-io-3.8.0/src/site/apt/filemappers.apt --- old/plexus-io-plexus-io-3.6.0/src/site/apt/filemappers.apt 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/site/apt/filemappers.apt 1970-01-01 01:00:00.000000000 +0100 @@ -1,122 +0,0 @@ - - ------------ - File Mappers - ------------ - -File Mappers - - A file mapper is a plexus component, which allows to convert file - names. File mappers are used when creating files. For example, - the {{{https://www.mojohaus.org/xml-maven-plugin}XML Maven Plugin}} - allows to specify a file mapper when creating files by XSLT - transformation. - - File mappers are implementing the interface - {{{./apidocs/org/codehaus/plexus/components/io/filemappers/FileMapper.html}FileMapper}}. - The idea of file mappers is borrowed from the - {{{https://ant.apache.org/manual/Types/mapper.html}Ant FileMapper}}, - which serves the same purpose within Ant. - - Available file mappers are - - * The {{{#Identity Mapper}Identity Mapper}}; it uses the role hints - "default", or "identity". - - * The {{{#File Extension Mapper}File Extension Mapper}}; its role hint - is "fileExtension". - - * The {{{#Flattening File Mapper}Flattening File Mapper}} with the role - hint of "flatten". - - * The {{{#Merging File Mapper}Merging File Mapper}}; its role hint is - "merge". - - * The {{{#Suffix File Mapper}Suffix File Mapper}}; its role hint is - "suffix". - -* {Identity Mapper} - - The {{{./apidocs/org/codehaus/plexus/components/io/filemappers/IdentityMapper.html}identity mapper}} - maps any file name to itself. This may be handy, where you want to avoid the value null for - file mappers. The identity takes no configuration parameters. - - For example, to use the identity mapper within the XML Maven Plugins <<<transform>>> - goal, you would use the following configuration snipped: - ------------------------------------------------------------------------------ - <fileMapper implementation="org.codehaus.plexus.components.io.filemappers.IdentityMapper"/> ------------------------------------------------------------------------------ - - The identity mapper uses the role hints "identity", or "default". - -* {File Extension Mapper} - - The {{{./apidocs/org/codehaus/plexus/components/io/filemappers/FileExtensionMapper.html}file - extension mapper}} changes the extension of the created files. For example, if you - would use the XML Maven Plugin to convert Docbook into FOP or PDF files, then you would - want the generated files to have the extension ".fo", or ".pdf". - - A configuration snippet for using the identity mapper within the XML Maven - Plugins <<<transform>>> goal would look like this: - ------------------------------------------------------------------------------ - <fileMapper implementation="org.codehaus.plexus.components.io.filemappers.FileExtensionMapper"> - <targetExtension>.pdf</targetExtension> - </fileMapper> ------------------------------------------------------------------------------ - - The file extension mapper uses the role hints "fileExtension". - -* {Flattening File Mapper} - - The {{{./apidocs/org/codehaus/plexus/components/io/filemappers/FlattenFileMapper.html}flattening - file mapper}} is used to flatten a directory structure: It removes all directory - components. For example, it would convert the name <<<META-INF/MANIFEST.MF>>> to - <<<MANIFEST.MF>>>. - - The flattening file mapper takes no configuration parameters. Consequently, - a typical configuration snippet would look like this: - ------------------------------------------------------------------------------ - <fileMapper implementation="org.codehaus.plexus.components.io.filemappers.FlattenFileMapper"/> ------------------------------------------------------------------------------ - - The flattening file mapper uses the role hint "flatten". - -* {Merging File Mapper} - - The {{{./apidocs/org/codehaus/plexus/components/io/filemappers/MergeFileMapper.html}merging - file mapper}} merges all possible file names into one file name. In other words, it - performs a constant mapping. For example, a merging file mapper, which maps all - possible file names to <<<theOneAndOnlyFile>>> would be configured as follows: - ------------------------------------------------------------------------------ - <fileMapper implementation="org.codehaus.plexus.components.io.filemappers.MergeFileMapper"> - <targetName>theOneAndOnlyFile</targetName> - </fileMapper> ------------------------------------------------------------------------------ - - The merging file mapper uses the role hint "merge". - -* {Suffix File Mapper} - - The {{{./apidocs/org/codehaus/plexus/components/io/filemappers/SuffixFileMapper.html}suffix - file mapper}} adds the given suffix to the filename. The suffix will be added before the file - extension. Examples : - ------------------------------------------------------------------------------ - theFile.txt => theFileNiceSuffix.txt - dir/file.java => dir/fileNiceSuffix.java - fileWithoutExtension => fileWithoutExtensionNiceSuffix - dir/archive.tar.gz => dir/archiveNiceSuffix.tar.gz ------------------------------------------------------------------------------ - - It would be configured as follows: - ------------------------------------------------------------------------------ - <fileMapper implementation="org.codehaus.plexus.components.io.filemappers.SuffixFileMapper"> - <suffix>NiceSuffix</suffix> - </fileMapper> ------------------------------------------------------------------------------ - - The suffix file mapper uses the role hint "suffix". diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/site/apt/fileselectors.apt new/plexus-io-plexus-io-3.8.0/src/site/apt/fileselectors.apt --- old/plexus-io-plexus-io-3.6.0/src/site/apt/fileselectors.apt 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/site/apt/fileselectors.apt 1970-01-01 01:00:00.000000000 +0100 @@ -1,63 +0,0 @@ - - -------------- - File Selectors - -------------- - -File Selectors - - A file selector is a plexus component, which allows to select certain - files out of a given set. For example, the {{{http://plexus.codehaus.org/plexus-archiver}Plexus Archiver}} - uses file selectors to select the files being archived out of a base directory. - Its counterpart, the Plexus Unarchiver allows to restrict the files to unarchive. - - File mappers are implementing the interface - {{{./apidocs/org/codehaus/plexus/components/io/fileselectors/FileSelector.html}FileSelector}}. - - Available file selectors are - - * The {{{#All Files Selector}All Files Selector}}; it uses the role hints - "default", or "all". - - * The {{{#Standard File Selector}Standard File Selector}}; its role hint - is "standard". - -* {All Files Selector} - - The {{{./apidocs/org/codehaus/plexus/components/io/fileselectors/AllFilesFileSelector.html}selector - for all files}} doesn't exclude any files. It is mainly useful when you want to avoid the - value null for a file selector. - - A configuration snippet for using the selector for all files would look like this: - ------------------------------------------------------------------------------ - <fileSelector implementation="org.codehaus.plexus.components.io.fileselectors.AllFilesFileSelector"/> ------------------------------------------------------------------------------ - - The selector for all files uses the role hints "all", or "default". - -* {Standard File Selector} - - The {{{./apidocs/org/codehaus/plexus/components/io/fileselectors/IncludeExcludeFileSelector} - standard file selector}} selects files based on include/exclude patterns. - - A configuration snippet for using the standard file selector would look like this: - ------------------------------------------------------------------------------ - <fileSelector implementation="org.codehaus.plexus.components.io.filemappers.FileExtensionMapper"> - <includes> - <include>**/*.gif</include> - <include>**/*.png</include> - <include>**/*.jpg</include> - <include>**/*.jpeg</include> - </includes> - <excludes> - <exclude>bar/</exclude> - </excludes> - <useDefaultExcludes>true</useDefaultExcludes> - <caseSensitive>false</caseSensitive> - </fileSelector> ------------------------------------------------------------------------------ - - This would include all image files, with the exception of those in the - directory <<<bar>>>. The default excludes (for example <<<CVS/>>>) would - apply and file names would be treated case insensitive. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/site/apt/index.apt new/plexus-io-plexus-io-3.8.0/src/site/apt/index.apt --- old/plexus-io-plexus-io-3.6.0/src/site/apt/index.apt 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/site/apt/index.apt 1970-01-01 01:00:00.000000000 +0100 @@ -1,28 +0,0 @@ - ------ - Introduction - ------ - -Plexus IO - - Plexus IO is a set of plexus components, which are designed for use - in I/O operations. These I/O operations are doing nothing spectacular. - For example, {{{http://jakarta.apache.org/commons/io}Commons IO}} is - a much more powerful library in the same area. However, the implementation - as a plexus component allows reuse in Maven. - - The following component groups are currently available: - - * {{{./filemappers.html}File Mappers}} - - * {{{./fileselectors.html}File Selectors}} - - [] - - Plexus IO components are typically very simple components, who could - very well live as part of the {{{http://plexus.codehaus.org/plexus-utils} - Plexus Utils}}. They do not, because Plexus Utils is a dependency of the - {{{http://plexus.codehaus.org/plexus-containers/plexus-container-default}Plexus Component API}}, - which is in turn a dependency of the Plexus IO test suite (of course, - a Plexus container is required to test components, even if they are - POJO's). In other words, Plexus Utils cannot contain components, because - that would introduce a circular dependency. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/site/markdown/filemappers.md new/plexus-io-plexus-io-3.8.0/src/site/markdown/filemappers.md --- old/plexus-io-plexus-io-3.6.0/src/site/markdown/filemappers.md 1970-01-01 01:00:00.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/site/markdown/filemappers.md 2026-09-19 02:25:52.000000000 +0200 @@ -0,0 +1,88 @@ +--- +title: File Mappers +--- + +# File Mappers + +A file mapper is a plexus component, which allows to convert file names. File mappers are used when creating files. For example, the [XML Maven Plugin](https://www.mojohaus.org/xml-maven-plugin) allows to specify a file mapper when creating files by XSLT transformation. + +File mappers are implementing the interface [FileMapper](./apidocs/org/codehaus/plexus/components/io/filemappers/FileMapper.html). The idea of file mappers is borrowed from the [Ant FileMapper](https://ant.apache.org/manual/Types/mapper.html), which serves the same purpose within Ant. + +Available file mappers are + +- The [Identity Mapper](#Identity_Mapper); it uses the role hints "default", or "identity". +- The [File Extension Mapper](#File_Extension_Mapper); its role hint is "fileExtension". +- The [Flattening File Mapper](#Flattening_File_Mapper) with the role hint of "flatten". +- The [Merging File Mapper](#Merging_File_Mapper); its role hint is "merge". +- The [Suffix File Mapper](#Suffix_File_Mapper); its role hint is "suffix". + +## <a id="Identity_Mapper"></a>Identity Mapper + +The [identity mapper](./apidocs/org/codehaus/plexus/components/io/filemappers/IdentityMapper.html) maps any file name to itself. This may be handy, where you want to avoid the value null for file mappers. The identity takes no configuration parameters. + +For example, to use the identity mapper within the XML Maven Plugins `transform` goal, you would use the following configuration snipped: + +``` +<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.IdentityMapper"/> +``` + +The identity mapper uses the role hints "identity", or "default". + +## <a id="File_Extension_Mapper"></a>File Extension Mapper + +The [file extension mapper](./apidocs/org/codehaus/plexus/components/io/filemappers/FileExtensionMapper.html) changes the extension of the created files. For example, if you would use the XML Maven Plugin to convert Docbook into FOP or PDF files, then you would want the generated files to have the extension ".fo", or ".pdf". + +A configuration snippet for using the identity mapper within the XML Maven Plugins `transform` goal would look like this: + +``` +<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.FileExtensionMapper"> + <targetExtension>.pdf</targetExtension> +</fileMapper> +``` + +The file extension mapper uses the role hints "fileExtension". + +## <a id="Flattening_File_Mapper"></a>Flattening File Mapper + +The [flattening file mapper](./apidocs/org/codehaus/plexus/components/io/filemappers/FlattenFileMapper.html) is used to flatten a directory structure: It removes all directory components. For example, it would convert the name `META-INF/MANIFEST.MF` to `MANIFEST.MF`. + +The flattening file mapper takes no configuration parameters. Consequently, a typical configuration snippet would look like this: + +``` +<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.FlattenFileMapper"/> +``` + +The flattening file mapper uses the role hint "flatten". + +## <a id="Merging_File_Mapper"></a>Merging File Mapper + +The [merging file mapper](./apidocs/org/codehaus/plexus/components/io/filemappers/MergeFileMapper.html) merges all possible file names into one file name. In other words, it performs a constant mapping. For example, a merging file mapper, which maps all possible file names to `theOneAndOnlyFile` would be configured as follows: + +``` +<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.MergeFileMapper"> + <targetName>theOneAndOnlyFile</targetName> +</fileMapper> +``` + +The merging file mapper uses the role hint "merge". + +## <a id="Suffix_File_Mapper"></a>Suffix File Mapper + +The [suffix file mapper](./apidocs/org/codehaus/plexus/components/io/filemappers/SuffixFileMapper.html) adds the given suffix to the filename. The suffix will be added before the file extension. Examples : + +``` +theFile.txt => theFileNiceSuffix.txt +dir/file.java => dir/fileNiceSuffix.java +fileWithoutExtension => fileWithoutExtensionNiceSuffix +dir/archive.tar.gz => dir/archiveNiceSuffix.tar.gz +``` + +It would be configured as follows: + +``` +<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.SuffixFileMapper"> + <suffix>NiceSuffix</suffix> +</fileMapper> +``` + +The suffix file mapper uses the role hint "suffix". diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/site/markdown/fileselectors.md new/plexus-io-plexus-io-3.8.0/src/site/markdown/fileselectors.md --- old/plexus-io-plexus-io-3.6.0/src/site/markdown/fileselectors.md 1970-01-01 01:00:00.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/site/markdown/fileselectors.md 2026-09-19 02:25:52.000000000 +0200 @@ -0,0 +1,50 @@ +--- +title: File Selectors +--- + +# File Selectors + +A file selector is a plexus component, which allows to select certain files out of a given set. For example, the [Plexus Archiver](http://plexus.codehaus.org/plexus-archiver) uses file selectors to select the files being archived out of a base directory. Its counterpart, the Plexus Unarchiver allows to restrict the files to unarchive. + +File mappers are implementing the interface [FileSelector](./apidocs/org/codehaus/plexus/components/io/fileselectors/FileSelector.html). + +Available file selectors are + +- The [All Files Selector](#All_Files_Selector); it uses the role hints "default", or "all". +- The [Standard File Selector](#Standard_File_Selector); its role hint is "standard". + +## <a id="All_Files_Selector"></a>All Files Selector + +The [selector for all files](./apidocs/org/codehaus/plexus/components/io/fileselectors/AllFilesFileSelector.html) doesn't exclude any files. It is mainly useful when you want to avoid the value null for a file selector. + +A configuration snippet for using the selector for all files would look like this: + +``` +<fileSelector implementation="org.codehaus.plexus.components.io.fileselectors.AllFilesFileSelector"/> +``` + +The selector for all files uses the role hints "all", or "default". + +## <a id="Standard_File_Selector"></a>Standard File Selector + +The [standard file selector](./apidocs/org/codehaus/plexus/components/io/fileselectors/IncludeExcludeFileSelector) selects files based on include/exclude patterns. + +A configuration snippet for using the standard file selector would look like this: + +``` +<fileSelector implementation="org.codehaus.plexus.components.io.filemappers.FileExtensionMapper"> + <includes> + <include>**/*.gif</include> + <include>**/*.png</include> + <include>**/*.jpg</include> + <include>**/*.jpeg</include> + </includes> + <excludes> + <exclude>bar/</exclude> + </excludes> + <useDefaultExcludes>true</useDefaultExcludes> + <caseSensitive>false</caseSensitive> +</fileSelector> +``` + +This would include all image files, with the exception of those in the directory `bar`. The default excludes (for example `CVS/`) would apply and file names would be treated case insensitive. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/site/markdown/index.md new/plexus-io-plexus-io-3.8.0/src/site/markdown/index.md --- old/plexus-io-plexus-io-3.6.0/src/site/markdown/index.md 1970-01-01 01:00:00.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/site/markdown/index.md 2026-09-19 02:25:52.000000000 +0200 @@ -0,0 +1,14 @@ +--- +title: Introduction +--- + +# Plexus IO + +Plexus IO is a set of plexus components, which are designed for use in I/O operations. These I/O operations are doing nothing spectacular. For example, [Commons IO](http://jakarta.apache.org/commons/io) is a much more powerful library in the same area. However, the implementation as a plexus component allows reuse in Maven. + +The following component groups are currently available: + +- [File Mappers](./filemappers.html) +- [File Selectors](./fileselectors.html) + +Plexus IO components are typically very simple components, who could very well live as part of the [Plexus Utils](http://plexus.codehaus.org/plexus-utils). They do not, because Plexus Utils is a dependency of the [Plexus Component API](http://plexus.codehaus.org/plexus-containers/plexus-container-default), which is in turn a dependency of the Plexus IO test suite (of course, a Plexus container is required to test components, even if they are POJO's). In other words, Plexus Utils cannot contain components, because that would introduce a circular dependency. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/test/java/org/codehaus/plexus/components/io/filemappers/ResourcesTest.java new/plexus-io-plexus-io-3.8.0/src/test/java/org/codehaus/plexus/components/io/filemappers/ResourcesTest.java --- old/plexus-io-plexus-io-3.6.0/src/test/java/org/codehaus/plexus/components/io/filemappers/ResourcesTest.java 2025-11-07 19:10:05.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/test/java/org/codehaus/plexus/components/io/filemappers/ResourcesTest.java 2026-09-19 02:25:52.000000000 +0200 @@ -132,11 +132,11 @@ } private void compare(PlexusIoResource res, File file) throws IOException { - assertNotSame(res.getLastModified(), PlexusIoResource.UNKNOWN_MODIFICATION_DATE); + assertNotSame(PlexusIoResource.UNKNOWN_MODIFICATION_DATE, res.getLastModified()); if (res instanceof PlexusIoFileResource) { assertEquals(res.getLastModified() / 1000, file.lastModified() / 1000); } - assertNotSame(res.getSize(), PlexusIoResource.UNKNOWN_RESOURCE_SIZE); + assertNotSame(PlexusIoResource.UNKNOWN_RESOURCE_SIZE, res.getSize()); assertEquals(res.getSize(), file.length()); InputStream in = res.getContents(); compare(in, file); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/test/java/org/codehaus/plexus/components/io/resources/HardLinkIdentityTest.java new/plexus-io-plexus-io-3.8.0/src/test/java/org/codehaus/plexus/components/io/resources/HardLinkIdentityTest.java --- old/plexus-io-plexus-io-3.6.0/src/test/java/org/codehaus/plexus/components/io/resources/HardLinkIdentityTest.java 1970-01-01 01:00:00.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/test/java/org/codehaus/plexus/components/io/resources/HardLinkIdentityTest.java 2026-09-19 02:25:52.000000000 +0200 @@ -0,0 +1,87 @@ +/* + * Copyright 2026 The plexus developers. + * + * 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.codehaus.plexus.components.io.resources; + +import java.io.ByteArrayInputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.codehaus.plexus.components.io.functions.ContentSupplier; +import org.codehaus.plexus.components.io.functions.HardLinkIdentitySupplier; +import org.codehaus.plexus.components.io.functions.NameSupplier; +import org.codehaus.plexus.components.io.resources.proxy.ProxyFactory; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +class HardLinkIdentityTest { + @TempDir + Path temp; + + /** Checks filesystem identity and invalidation when effective content changes. */ + @Test + void identifiesOnlyOriginalContent() throws Exception { + Path first = Files.write(temp.resolve("first"), new byte[] {1}); + Path second = temp.resolve("second"); + try { + Files.createLink(second, first); + } catch (java.io.IOException | UnsupportedOperationException e) { + assumeTrue(false, "Hard links unavailable: " + e); + } + PlexusIoResource a = ResourceFactory.createResource(first.toFile()); + PlexusIoResource b = ResourceFactory.createResource(second.toFile()); + Object identity = ((HardLinkIdentitySupplier) a).getHardLinkIdentity(); + assumeTrue(identity != null, "Filesystem does not expose file keys"); + assertEquals(identity, ((HardLinkIdentitySupplier) b).getHardLinkIdentity()); + PlexusIoResource transformed = ResourceFactory.createResource( + first.toFile(), (resource, input) -> new ByteArrayInputStream(new byte[] {2})); + assertNull(((HardLinkIdentitySupplier) transformed).getHardLinkIdentity()); + PlexusIoResource supplied = ResourceFactory.createResource( + first.toFile(), + "custom", + () -> new ByteArrayInputStream(new byte[] {3}), + (org.codehaus.plexus.components.io.functions.InputStreamTransformer) null); + assertNull(((HardLinkIdentitySupplier) supplied).getHardLinkIdentity()); + } + + /** Transparent renaming keeps identity while a replacement stream invalidates it. */ + @Test + void proxiesRespectContentOverrides() throws Exception { + Path file = Files.write(temp.resolve("file"), new byte[] {1}); + PlexusIoResource original = ResourceFactory.createResource(file.toFile()); + Object identity = ((HardLinkIdentitySupplier) original).getHardLinkIdentity(); + PlexusIoResource renamed = ProxyFactory.createProxy(original, (NameSupplier) () -> "renamed"); + assertEquals(identity, ((HardLinkIdentitySupplier) renamed).getHardLinkIdentity()); + PlexusIoResource replaced = + ProxyFactory.createProxy(original, (ContentSupplier) () -> new ByteArrayInputStream(new byte[] {2})); + assertNull(((HardLinkIdentitySupplier) replaced).getHardLinkIdentity()); + } + + /** Collection resolution must explicitly preserve or discard the source guarantee. */ + @Test + void deferredResourcesRespectTransformers() throws Exception { + Path file = Files.write(temp.resolve("file"), new byte[] {1}); + PlexusIoResource original = ResourceFactory.createResource(file.toFile()); + Object identity = ((HardLinkIdentitySupplier) original).getHardLinkIdentity(); + PlexusIoResourceCollection owner = new PlexusIoFileResourceCollection(); + assertEquals( + identity, + ((HardLinkIdentitySupplier) new Deferred(original, owner, false).asResource()).getHardLinkIdentity()); + assertNull(((HardLinkIdentitySupplier) new Deferred(original, owner, true).asResource()).getHardLinkIdentity()); + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/plexus-io-plexus-io-3.6.0/src/test/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollectionTest.java new/plexus-io-plexus-io-3.8.0/src/test/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollectionTest.java --- old/plexus-io-plexus-io-3.6.0/src/test/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollectionTest.java 1970-01-01 01:00:00.000000000 +0100 +++ new/plexus-io-plexus-io-3.8.0/src/test/java/org/codehaus/plexus/components/io/resources/PlexusIoFileResourceCollectionTest.java 2026-09-19 02:25:52.000000000 +0200 @@ -0,0 +1,154 @@ +package org.codehaus.plexus.components.io.resources; + +/* + * Copyright 2007 The Codehaus 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. + */ + +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.io.TempDir; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class PlexusIoFileResourceCollectionTest { + private static final File SYMLINKS = new File("src/test/resources/symlinks/src"); + + @Test + @DisabledOnOs(OS.WINDOWS) + void followedSymlinkToFileIsCollectedAsFile() throws Exception { + PlexusIoFileResourceCollection collection = collection(true, "symR"); + + PlexusIoResource resource = single(collection); + + assertFalse(resource.isSymbolicLink(), "symR should not be reported as a symbolic link"); + assertInstanceOf(PlexusIoFileResource.class, resource); + assertEquals("This file is a source. r r r filemode\n", contentOf(resource)); + } + + @Test + @DisabledOnOs(OS.WINDOWS) + void unfollowedSymlinkToFileIsCollectedAsSymlink() throws Exception { + PlexusIoFileResourceCollection collection = collection(false, "symR"); + + PlexusIoResource resource = single(collection); + + assertTrue(resource.isSymbolicLink(), "symR should be reported as a symbolic link"); + assertInstanceOf(PlexusIoSymlinkResource.class, resource); + assertEquals("fileR.txt", ((PlexusIoSymlinkResource) resource).getSymlinkDestination()); + } + + @Test + @DisabledOnOs(OS.WINDOWS) + void followedSymlinkToDirectoryIsTraversed() throws Exception { + PlexusIoFileResourceCollection collection = collection(true, "symDir/**"); + + List<PlexusIoResource> resources = all(collection); + + assertEquals(asList("symDir", "symDir/targetFile.txt"), names(resources)); + for (PlexusIoResource resource : resources) { + assertFalse(resource.isSymbolicLink(), resource.getName() + " should not be reported as a symbolic link"); + } + assertInstanceOf(PlexusIoFileResource.class, resources.get(1)); + } + + @Test + @DisabledOnOs(OS.WINDOWS) + void unfollowedSymlinkToDirectoryIsNotTraversed() throws Exception { + PlexusIoFileResourceCollection collection = collection(false, "symDir/**"); + + List<PlexusIoResource> resources = all(collection); + + assertEquals(asList("symDir"), names(resources)); + assertTrue(resources.get(0).isSymbolicLink()); + } + + /** + * A dangling symbolic link has no attributes to follow. Collecting it must not fail the whole scan. + */ + @Test + @DisabledOnOs(OS.WINDOWS) + void danglingSymlinkDoesNotBreakCollection(@TempDir Path tempDir) throws Exception { + Files.write(tempDir.resolve("aRegularFile.txt"), "content\n".getBytes(UTF_8)); + Files.createSymbolicLink(tempDir.resolve("dangling"), Paths.get("noSuchFile.txt")); + + PlexusIoFileResourceCollection collection = new PlexusIoFileResourceCollection(); + collection.setBaseDir(tempDir.toFile()); + collection.setFollowingSymLinks(true); + + List<String> names = new ArrayList<>(); + for (Iterator<PlexusIoResource> it = collection.getResources(); it.hasNext(); ) { + names.add(it.next().getName()); + } + + assertTrue(names.contains("aRegularFile.txt"), "regular files must still be collected, got " + names); + } + + private static PlexusIoFileResourceCollection collection(boolean followSymlinks, String include) { + PlexusIoFileResourceCollection collection = new PlexusIoFileResourceCollection(); + collection.setBaseDir(SYMLINKS); + collection.setFollowingSymLinks(followSymlinks); + collection.setIncludes(new String[] {include}); + return collection; + } + + private static List<PlexusIoResource> all(PlexusIoFileResourceCollection collection) throws Exception { + List<PlexusIoResource> resources = new ArrayList<>(); + for (Iterator<PlexusIoResource> it = collection.getResources(); it.hasNext(); ) { + resources.add(it.next()); + } + return resources; + } + + private static PlexusIoResource single(PlexusIoFileResourceCollection collection) throws Exception { + List<PlexusIoResource> resources = all(collection); + assertEquals(1, resources.size(), "expected exactly one resource, got " + resources); + return resources.get(0); + } + + private static List<String> names(List<PlexusIoResource> resources) { + List<String> names = new ArrayList<>(); + for (PlexusIoResource resource : resources) { + names.add(resource.getName()); + } + return names; + } + + private static String contentOf(PlexusIoResource resource) throws Exception { + try (InputStream in = resource.getContents()) { + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + int n; + while ((n = in.read(buf)) != -1) { + out.write(buf, 0, n); + } + return new String(out.toByteArray(), UTF_8); + } + } +} ++++++ plexus-io-build.xml ++++++ --- /var/tmp/diff_new_pack.bgxhDJ/_old 2026-09-29 17:50:48.678791544 +0200 +++ /var/tmp/diff_new_pack.bgxhDJ/_new 2026-09-29 17:50:48.686791878 +0200 @@ -10,7 +10,7 @@ <property name="project.artifactId" value="plexus-io"/> <property name="project.groupId" value="org.codehaus.plexus"/> - <property name="project.version" value="3.6.0"/> + <property name="project.version" value="3.8.0"/> <property name="compiler.release" value="8"/> <property name="compiler.source" value="1.${compiler.release}"/> @@ -126,6 +126,8 @@ basedir="${build.outputDir}" excludes="**/package.html"> <manifest> + <attribute name="Automatic-Module-Name" + value="org.codehaus.plexus.components.io"/> <attribute name="JavaPackages-ArtifactId" value="${project.artifactId}"/> <attribute name="JavaPackages-GroupId" value="${project.groupId}"/> <attribute name="JavaPackages-Version" value="${project.version}"/>
