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
 
-[![Build 
Status](https://github.com/codehaus-plexus/plexus-io/actions/workflows/maven.yml/badge.svg)](https://github.com/codehaus-plexus/plexus-io/actions/workflows/maven.yml)
-[![Maven 
Central](https://img.shields.io/maven-central/v/org.codehaus.plexus/plexus-io.svg?label=Maven%20Central)](https://search.maven.org/artifact/org.codehaus.plexus/plexus-io)
+[![Maven 
Central](https://img.shields.io/maven-central/v/org.codehaus.plexus/plexus-io.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/org.codehaus.plexus/plexus-io)
+[![GitHub 
CI](https://github.com/codehaus-plexus/plexus-io/actions/workflows/maven.yml/badge.svg)](https://github.com/codehaus-plexus/plexus-io/actions/workflows/maven.yml)
 [![Reproducible 
Builds](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/org/codehaus/plexus/plexus-io/badge.json)](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}"/>

Reply via email to