filenameComparator) {
delegate.setFilenameComparator(filenameComparator);
}
- @Override
- @Deprecated
- public void configureReproducible(Date outputTimestamp) {
- delegate.configureReproducible(outputTimestamp);
- }
-
@Override
public void setOverrideUid(int uid) {
delegate.setOverrideUid(uid);
diff --git a/src/main/java/org/apache/maven/plugins/assembly/filter/ContainerDescriptorHandler.java b/src/main/java/org/apache/maven/plugins/assembly/filter/ContainerDescriptorHandler.java
index 087b3c64..ca11a131 100644
--- a/src/main/java/org/apache/maven/plugins/assembly/filter/ContainerDescriptorHandler.java
+++ b/src/main/java/org/apache/maven/plugins/assembly/filter/ContainerDescriptorHandler.java
@@ -22,6 +22,11 @@
import org.codehaus.plexus.components.io.fileselectors.FileSelector;
/**
- *
+ * Customizes archive contents through selection and finalization callbacks.
+ * Handlers that call APIs removed by Plexus Archiver 5 must migrate and be recompiled against the
+ * version used by this plugin. In particular, use {@code Archiver.getResources()} instead of
+ * {@code Archiver.getFiles()}, and the {@code FileTime} timestamp methods instead of the removed
+ * {@code Date} methods. Assembly's proxy follows the Archiver 5 API and does not provide compatibility
+ * shims for removed methods.
*/
public interface ContainerDescriptorHandler extends ArchiveFinalizer, FileSelector {}
diff --git a/src/main/java/org/apache/maven/plugins/assembly/mojos/AbstractAssemblyMojo.java b/src/main/java/org/apache/maven/plugins/assembly/mojos/AbstractAssemblyMojo.java
index 2b58dca6..632fb0c4 100644
--- a/src/main/java/org/apache/maven/plugins/assembly/mojos/AbstractAssemblyMojo.java
+++ b/src/main/java/org/apache/maven/plugins/assembly/mojos/AbstractAssemblyMojo.java
@@ -310,7 +310,11 @@ public abstract class AbstractAssemblyMojo extends AbstractMojo implements Assem
*
* <appxml>${project.basedir}/somepath/app.xml</appxml>
*
- *
+ * For TAR and compressed TAR formats, hard-link preservation can be enabled with
+ * {@code true}. It is disabled by default.
+ * Eligible, untransformed files with the same hard-link identity and compatible output metadata share one
+ * payload; subsequent entries refer to the earlier entry's final archive name. Filtering and line-ending
+ * transformations keep contents independent. Configure this option only on executions producing TAR formats.
*
* @since 2.2-beta-3
*/
diff --git a/src/site/markdown/examples/single/index.md b/src/site/markdown/examples/single/index.md
index 6c141dc8..9467db1d 100644
--- a/src/site/markdown/examples/single/index.md
+++ b/src/site/markdown/examples/single/index.md
@@ -31,6 +31,7 @@ A Single Project is a project whose assemblies do not use <moduleSets>.
The examples below may help you in creating assemblies for your single projects.
- [Filtering Some Distribution Files](./filtering-some-distribution-files.html)
+- [Preserving TAR Hard Links](./preserving-tar-hard-links.html)
- [Including/Excluding Artifacts](./including-and-excluding-artifacts.html)
- [Using Component Descriptors](./using-components.html)
- [Using Container Descriptor Handlers](./using-container-descriptor-handlers.html)
diff --git a/src/site/markdown/examples/single/preserving-tar-hard-links.md b/src/site/markdown/examples/single/preserving-tar-hard-links.md
new file mode 100644
index 00000000..f2444832
--- /dev/null
+++ b/src/site/markdown/examples/single/preserving-tar-hard-links.md
@@ -0,0 +1,124 @@
+
+
+# Preserving TAR Hard Links
+
+Hard-link preservation is **disabled by default**. Without configuration, each
+selected regular file is stored with its own contents, even when source names
+refer to the same inode.
+
+To enable preservation, use the existing `archiverConfig` parameter in an
+execution that produces only TAR formats:
+
+```xml
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.8.1-SNAPSHOT
+
+
+ src/assembly/distribution.xml
+
+
+ true
+
+
+
+
+ distribution
+ package
+
+ single
+
+
+
+
+```
+
+For example, `src/assembly/distribution.xml` can contain:
+
+```xml
+
+ distribution
+
+ tar
+ tar.gz
+
+
+
+ src/distribution
+ payload
+
+
+
+```
+
+The option applies to plain TAR and all supported compressed TAR formats:
+`tar.gz`/`tgz`, `tar.bz2`/`tbz2`, `tar.xz`/`txz`, `tar.snappy`, and `tar.zst`.
+Set `preserveHardLinks` to `false`, or omit it, to store each file independently.
+For an assembly that also produces ZIP, JAR, or other formats, use a separate
+execution for TAR: the reflective option belongs to the TAR archiver.
+
+## What is preserved
+
+When selected source names are hard links to the same regular file and have
+compatible output metadata, the first eligible entry stores the contents.
+Subsequent aliases contain zero-length hard-link headers referring to that earlier
+entry. GNU tar and bsdtar extract these entries as files sharing an inode.
+Targets use the final archive names, including the assembly base directory,
+`outputDirectory`, and `destName` mappings.
+
+Preservation requires a known identity for untransformed resource contents.
+Unavailable filesystem file keys, custom content suppliers, filtering, line-ending
+conversion, and incompatible output metadata cause entries to be stored with
+independent payloads. Filtering remains independent even if the resulting bytes
+happen to match. Unrelated files with identical bytes are not combined. Symbolic
+links retain their usual representation. `tarLongFileMode=truncate` disables
+hard-link preservation because truncated target names may be ambiguous.
+
+Hard-link headers refer to paths. As with GNU tar and bsdtar, a later write through
+a directory symlink can replace the contents at a target path before an alias is
+extracted. Avoid overlapping output paths when the original contents must be
+retained for every alias. See the
+[Plexus Archiver hard-link documentation](https://codehaus-plexus.github.io/plexus-archiver/hard-links.html)
+for writer and extraction semantics.
+
+## Development dependencies and Java requirement
+
+This development version uses `plexus-archiver:5.0.0-SNAPSHOT` and
+`plexus-io:3.7.1-SNAPSHOT`, containing
+[Plexus Archiver PR #493](https://github.com/codehaus-plexus/plexus-archiver/pull/493)
+and [Plexus IO PR #191](https://github.com/codehaus-plexus/plexus-io/pull/191).
+Until releases containing both changes are available, install the companion IO
+branch first with `mvn install`, then the Archiver branch, before building Assembly.
+Both dependency versions must be replaced with released versions before an
+Assembly release; an ordinary build cannot fetch these locally built PR snapshots
+from Maven Central.
+
+Plexus Archiver 5 requires Java 17. Consequently, this Assembly development version
+requires Maven to run on Java 17 or newer, including when preservation is disabled.
+Maven 3.9.6 or newer is also required: older Maven versions bundle a Sisu injector
+that cannot discover these Java 17 components. Adopting this dependency therefore
+raises the requirements from Assembly 3.8.0's Java 8 and Maven 3.6.3 baseline.
+
+The Archiver 5 upgrade also removes deprecated methods from the `Archiver`
+interface. Existing custom handlers that call those methods must be migrated and
+recompiled. See
+[Migrating custom handlers to Plexus Archiver 5](./using-container-descriptor-handlers.html#migrating-custom-handlers-to-plexus-archiver-5)
+for the supported replacement APIs.
diff --git a/src/site/markdown/examples/single/using-container-descriptor-handlers.md.vm b/src/site/markdown/examples/single/using-container-descriptor-handlers.md.vm
index 62d8612d..39addf0c 100644
--- a/src/site/markdown/examples/single/using-container-descriptor-handlers.md.vm
+++ b/src/site/markdown/examples/single/using-container-descriptor-handlers.md.vm
@@ -89,6 +89,38 @@ The plugin comes with several handlers already defined.
</assembly>
+## Migrating custom handlers to Plexus Archiver 5
+
+Assembly 3.8.1-SNAPSHOT uses Plexus Archiver 5, which removes deprecated methods
+from the `Archiver` interface. Existing custom handlers that call these methods
+**must update their source and be recompiled** against this Assembly version.
+
+A handler compiled against Archiver 4 can otherwise fail with `NoSuchMethodError`
+when invoking `Archiver.getFiles()` or `Archiver.setLastModifiedDate(Date)`.
+`AssemblyProxyArchiver` follows the Archiver 5 API and provides no compatibility
+shims for these removed methods. Handlers should use the current `Archiver`
+interface rather than cast to Assembly's concrete proxy.
+
+| Removed interface API | Replacement |
+| --- | --- |
+| `getFiles()` | Iterate `getResources()`; construct a map explicitly if needed. |
+| `setLastModifiedDate(Date)` | `setLastModifiedTime(FileTime)`; use `FileTime.fromMillis(date.getTime())`, or `null` to retain source timestamps. |
+| `getLastModifiedDate()` | `getLastModifiedTime()`; handle `null` before converting with `new Date(time.toMillis())`. |
+| `configureReproducible(Date)` | `configureReproducibleBuild(FileTime)` with a non-null timestamp. |
+| `addDirectory(File, ...)` | Configure a `DefaultFileSet` and call `addFileSet(FileSet)`. |
+| `addArchivedFileSet(File, ...)` | Configure a `DefaultArchivedFileSet` and call `addArchivedFileSet(ArchivedFileSet)`. |
+| `isUseJvmChmod()` / `setUseJvmChmod(boolean)` | Remove these calls; permission changes use the JVM. |
+
+Resource iteration preserves archive order and can return directories, symbolic
+links, and repeated names. Unlike the old map, it does not collapse duplicate
+names. A replacement map should honor `getIncludeEmptyDirs()`, include regular
+files when that setting is false, and retain the last entry for each repeated
+name to reproduce the old map view.
+
+After migrating, test the handler through its `finalizeArchiveCreation(Archiver)`
+callback with the plugin's dependencies. Tests of concrete proxy calls alone do
+not verify the interface calls made by a custom handler.
+
Custom container descriptor handlers
------------------------------------
diff --git a/src/site/site.xml b/src/site/site.xml
index 25fbac34..2dd3286f 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -52,6 +52,7 @@ under the License.
-
+
diff --git a/src/test/java/org/apache/maven/plugins/assembly/archive/DefaultAssemblyArchiverTest.java b/src/test/java/org/apache/maven/plugins/assembly/archive/DefaultAssemblyArchiverTest.java
index bf3a0e3c..b3795d5f 100644
--- a/src/test/java/org/apache/maven/plugins/assembly/archive/DefaultAssemblyArchiverTest.java
+++ b/src/test/java/org/apache/maven/plugins/assembly/archive/DefaultAssemblyArchiverTest.java
@@ -205,6 +205,31 @@ void createArchiverShouldConfigureArchiver() throws Exception {
verify(archiverManager).getArchiver("dummy");
}
+ /** Verifies that the existing reflective configuration can enable and explicitly disable TAR hard links. */
+ @Test
+ void createArchiverShouldConfigureHardLinkPreservation() throws Exception {
+ for (boolean preserve : new boolean[] {true, false}) {
+ final TarArchiver tarArchiver = new TarArchiver();
+ // Start from the opposite value so both configuration values must reach the real setter.
+ tarArchiver.setPreserveHardLinks(!preserve);
+ when(archiverManager.getArchiver("tar")).thenReturn(tarArchiver);
+
+ final AssemblerConfigurationSource configSource = mock(AssemblerConfigurationSource.class);
+ when(configSource.getProject()).thenReturn(new MavenProject(new Model()));
+ when(configSource.getTarLongFileMode()).thenReturn(TarLongFileMode.fail.toString());
+ when(configSource.getWorkingDirectory()).thenReturn(temporaryFolder);
+ when(configSource.getArchiverConfig())
+ .thenReturn(
+ "" + preserve + "");
+ setupInterpolators(configSource);
+
+ createSubject(Collections.emptyList())
+ .createArchiver("tar", false, "finalName", configSource, Collections.emptyList(), null);
+
+ assertEquals(preserve, tarArchiver.isPreserveHardLinks());
+ }
+ }
+
@Test
void createArchiverShouldCreateTarArchiverWithNoCompression() throws Exception {
final TestTarArchiver ttArchiver = new TestTarArchiver();
@@ -225,6 +250,7 @@ void createArchiverShouldCreateTarArchiverWithNoCompression() throws Exception {
subject.createArchiver("tar", false, "finalName", configSource, null, null);
assertNull(ttArchiver.compressionMethod);
+ assertFalse(ttArchiver.isPreserveHardLinks());
assertEquals(TarLongFileMode.fail, ttArchiver.longFileMode);
// result of easymock migration, should be assert of expected result instead of verifying methodcalls
diff --git a/src/test/java/org/apache/maven/plugins/assembly/archive/archiver/AssemblyProxyArchiverTest.java b/src/test/java/org/apache/maven/plugins/assembly/archive/archiver/AssemblyProxyArchiverTest.java
index 032e914e..6b7c2f3a 100644
--- a/src/test/java/org/apache/maven/plugins/assembly/archive/archiver/AssemblyProxyArchiverTest.java
+++ b/src/test/java/org/apache/maven/plugins/assembly/archive/archiver/AssemblyProxyArchiverTest.java
@@ -133,8 +133,7 @@ void addFileNoPermsCallAcceptFilesOnlyOnce() throws Exception {
}
@Test
- @SuppressWarnings("deprecation")
- void addDirectoryNoPermsCallAcceptFilesOnlyOnce() throws Exception {
+ void addFileSetNoPermsCallAcceptFilesOnlyOnce() throws Exception {
final Archiver delegate = new JarArchiver();
final File output = File.createTempFile("junit", null, temporaryFolder);
@@ -154,7 +153,7 @@ void addDirectoryNoPermsCallAcceptFilesOnlyOnce() throws Exception {
Files.write(
dir.toPath().resolve("file.txt"), Collections.singletonList("This is a test."), StandardCharsets.UTF_8);
- archiver.addDirectory(dir);
+ archiver.addFileSet(DefaultFileSet.fileSet(dir));
archiver.createArchive();