diff --git a/src/main/java/org/codehaus/plexus/archiver/dir/DirectoryArchiver.java b/src/main/java/org/codehaus/plexus/archiver/dir/DirectoryArchiver.java index 0b38f966a..1034c83c6 100644 --- a/src/main/java/org/codehaus/plexus/archiver/dir/DirectoryArchiver.java +++ b/src/main/java/org/codehaus/plexus/archiver/dir/DirectoryArchiver.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.attribute.FileTime; import java.util.ArrayList; @@ -42,6 +43,15 @@ public class DirectoryArchiver extends AbstractArchiver { private final List directoryChmods = new ArrayList<>(); + private long filesCopied; + + /** + * Default constructor. + */ + public DirectoryArchiver() { + // preserver default behavior + setForced(false); + } public void resetArchiver() throws IOException { cleanUp(); @@ -67,7 +77,7 @@ public void execute() throws ArchiverException, IOException { throw new ArchiverException(destDirectory + " is not writable."); } - getLogger().info("Copying files to " + destDirectory.getAbsolutePath()); + getLogger().info("Copying files to {}", destDirectory.getAbsolutePath()); try { while (iter.hasNext()) { @@ -93,6 +103,20 @@ public void execute() throws ArchiverException, IOException { directoryChmods.forEach(Runnable::run); directoryChmods.clear(); + + if (filesCopied > 0) { + getLogger() + .info( + "{} file{} copied to {}", + filesCopied, + filesCopied > 0 ? "s" : "", + destDirectory.getAbsolutePath()); + } else { + getLogger().info("All files are uptodate in {}", destDirectory.getAbsolutePath()); + } + + filesCopied = 0; + } catch (final IOException ioe) { final String message = "Problem copying files : " + ioe.getMessage(); throw new ArchiverException(message, ioe); @@ -118,15 +142,20 @@ protected void copyFile(final ArchiveEntry entry, final String vPath) throws Arc final File outFile = new File(vPath); final long inLastModified = in.getLastModified(); - final long outLastModified = outFile.lastModified(); - if (ResourceUtils.isUptodate(inLastModified, outLastModified)) { - return; + + if (!isForced()) { + final long outLastModified = outFile.lastModified(); + if (ResourceUtils.isUptodate(inLastModified, outLastModified)) { + return; + } } if (!in.isDirectory()) { makeParentDirectories(outFile); - ResourceUtils.copyFile(entry.getInputStream(), outFile); - + try (InputStream input = entry.getInputStream()) { + ResourceUtils.copyFile(input, outFile); + } + filesCopied++; setFileModes(entry, outFile, inLastModified); } else { // file is a directory if (outFile.exists()) { @@ -191,4 +220,9 @@ protected void close() throws IOException {} protected String getArchiveType() { return "directory"; } + + @Override + public boolean isSupportingForced() { + return true; + } } diff --git a/src/main/java/org/codehaus/plexus/archiver/util/ResourceUtils.java b/src/main/java/org/codehaus/plexus/archiver/util/ResourceUtils.java index 1bd25e01f..b03a7e76c 100644 --- a/src/main/java/org/codehaus/plexus/archiver/util/ResourceUtils.java +++ b/src/main/java/org/codehaus/plexus/archiver/util/ResourceUtils.java @@ -3,12 +3,12 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import org.codehaus.plexus.components.io.functions.FileSupplier; import org.codehaus.plexus.components.io.resources.PlexusIoResource; -import org.codehaus.plexus.util.IOUtil; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; /** * Utility class for work with {@link PlexusIoResource} instances. @@ -35,16 +35,7 @@ public static boolean isUptodate(PlexusIoResource source, File destination) { * the given modification date. */ public static boolean isUptodate(PlexusIoResource source, long destinationDate) { - final long s = source.getLastModified(); - if (s == PlexusIoResource.UNKNOWN_MODIFICATION_DATE) { - return false; - } - - if (destinationDate == 0) { - return false; - } - - return destinationDate > s; + return isUptodate(source.getLastModified(), destinationDate); } /** @@ -60,16 +51,15 @@ public static boolean isUptodate(long sourceDate, long destinationDate) { return false; } - return destinationDate > sourceDate; + return destinationDate >= sourceDate; } /** * Copies the sources contents to the given destination file. */ public static void copyFile(PlexusIoResource in, File outFile) throws IOException { - try (InputStream input = in.getContents(); - OutputStream output = Files.newOutputStream(outFile.toPath())) { - IOUtil.copy(input, output); + try (InputStream input = in.getContents()) { + Files.copy(input, outFile.toPath(), REPLACE_EXISTING); } } @@ -77,18 +67,7 @@ public static void copyFile(PlexusIoResource in, File outFile) throws IOExceptio * Copies the sources contents to the given destination file. */ public static void copyFile(InputStream input, File outFile) throws IOException { - OutputStream output = null; - try { - output = Files.newOutputStream(outFile.toPath()); - IOUtil.copy(input, output); - output.close(); - output = null; - input.close(); - input = null; - } finally { - IOUtil.close(input); - IOUtil.close(output); - } + Files.copy(input, outFile.toPath(), REPLACE_EXISTING); } /** diff --git a/src/test/java/org/codehaus/plexus/archiver/jar/DirectoryArchiverUnpackJarTest.java b/src/test/java/org/codehaus/plexus/archiver/jar/DirectoryArchiverUnpackJarTest.java index fe11d17e8..61cc037b4 100644 --- a/src/test/java/org/codehaus/plexus/archiver/jar/DirectoryArchiverUnpackJarTest.java +++ b/src/test/java/org/codehaus/plexus/archiver/jar/DirectoryArchiverUnpackJarTest.java @@ -2,11 +2,17 @@ import javax.annotation.Nonnull; +import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileTime; +import org.apache.commons.io.FileUtils; import org.codehaus.plexus.archiver.Archiver; import org.codehaus.plexus.archiver.TestSupport; import org.codehaus.plexus.archiver.util.ArchiveEntryUtils; @@ -15,7 +21,7 @@ import org.codehaus.plexus.components.io.resources.PlexusIoResource; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; class DirectoryArchiverUnpackJarTest extends TestSupport { @@ -36,25 +42,94 @@ public InputStream transform(@Nonnull PlexusIoResource resource, @Nonnull InputS @Test void test_dependency_sets_depSet_unpacked_rdonly() throws Exception { File src = new File("src/test/resources/unpack_issue.jar"); - assertTrue(src.exists()); + File dest = new File("target/depset_unpack"); + FileUtils.deleteDirectory(dest); + assertThat(src).isFile(); + + Archiver archiver = createArchiver(src, dest); + archiver.setDefaultDirectoryMode(0555); + archiver.setDirectoryMode(0555); // causes permission denied if bug is not fixed. + archiver.createArchive(); + assertThat(new File(dest, "child-1/META-INF/MANIFEST.MF")).isFile(); + + // make them writeable or mvn clean will fail + ArchiveEntryUtils.chmod(new File(dest, "child-1/META-INF"), 0777); + ArchiveEntryUtils.chmod(new File(dest, "child-1/META-INF/maven"), 0777); + ArchiveEntryUtils.chmod(new File(dest, "child-1/META-INF/maven/test"), 0777); + ArchiveEntryUtils.chmod(new File(dest, "child-1/META-INF/maven/test/child1"), 0777); + ArchiveEntryUtils.chmod(new File(dest, "child-1/assembly-resources"), 0777); + } + + @Test + void test_dependency_sets_depSet_unpacked_by_default_dont_override() throws Exception { + + File src = new File("src/test/resources/unpack_issue.jar"); + File dest = new File("target/depset_unpack_dont_override"); + FileUtils.deleteDirectory(dest); + + Archiver archiver = createArchiver(src, dest); + archiver.createArchive(); + + File manifestFile = new File(dest, "child-1/META-INF/MANIFEST.MF"); + assertThat(manifestFile).content().hasLineCount(6); + + // change content of one file + overwriteFileContent(manifestFile.toPath()); + assertThat(manifestFile).content().hasLineCount(1); + + archiver = createArchiver(src, dest); + archiver.createArchive(); + + // content was not changed + assertThat(manifestFile).content().hasLineCount(1); + } + + @Test + void test_dependency_sets_depSet_force_unpacked() throws Exception { + + File src = new File("src/test/resources/unpack_issue.jar"); + File dest = new File("target/depset_unpack_force"); + FileUtils.deleteDirectory(dest); + + Archiver archiver = createArchiver(src, dest); + archiver.createArchive(); + + File manifestFile = new File(dest, "child-1/META-INF/MANIFEST.MF"); + assertThat(manifestFile).content().hasLineCount(6); + + // change content of one file + overwriteFileContent(manifestFile.toPath()); + assertThat(manifestFile).content().hasLineCount(1); + + archiver = createArchiver(src, dest); + archiver.setForced(true); + archiver.createArchive(); + + // content was changed + assertThat(manifestFile).content().hasLineCount(6); + } + + private Archiver createArchiver(File src, File dest) { + assertThat(src).isFile(); DefaultArchivedFileSet afs = DefaultArchivedFileSet.archivedFileSet(src); afs.setIncludes(DEFAULT_INCLUDES_ARRAY); afs.setExcludes(null); afs.setPrefix("child-1/"); afs.setStreamTransformer(new IdentityTransformer()); - Archiver archiver = (Archiver) lookup(Archiver.class, "dir"); - archiver.setDefaultDirectoryMode(0555); - archiver.setDirectoryMode(0555); // causes permission denied if bug is not fixed. - archiver.setDestFile(new File("target/depset_unpack")); - archiver.addArchivedFileSet(afs, Charset.forName("UTF-8")); - archiver.createArchive(); - assertTrue(new File("target/depset_unpack/child-1/META-INF/MANIFEST.MF").exists()); + Archiver archiver = lookup(Archiver.class, "dir"); + archiver.setDestFile(dest); + archiver.addArchivedFileSet(afs, StandardCharsets.UTF_8); + return archiver; + } - // make them writeable or mvn clean will fail - ArchiveEntryUtils.chmod(new File("target/depset_unpack/child-1/META-INF"), 0777); - ArchiveEntryUtils.chmod(new File("target/depset_unpack/child-1/META-INF/maven"), 0777); - ArchiveEntryUtils.chmod(new File("target/depset_unpack/child-1/META-INF/maven/test"), 0777); - ArchiveEntryUtils.chmod(new File("target/depset_unpack/child-1/META-INF/maven/test/child1"), 0777); - ArchiveEntryUtils.chmod(new File("target/depset_unpack/child-1/assembly-resources"), 0777); + private void overwriteFileContent(Path path) throws IOException { + FileTime lastModifiedTime = Files.getLastModifiedTime(path); + + try (BufferedWriter writer = + Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING)) { + writer.write("TEST123"); + } + + Files.setLastModifiedTime(path, lastModifiedTime); } } diff --git a/src/test/java/org/codehaus/plexus/archiver/util/ResourceUtilsTest.java b/src/test/java/org/codehaus/plexus/archiver/util/ResourceUtilsTest.java new file mode 100644 index 000000000..bcd43da24 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/archiver/util/ResourceUtilsTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 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.archiver.util; + +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class ResourceUtilsTest { + + public static Stream testIsUpToDate() { + return Stream.of( + Arguments.of(0, 0, false), // dest and src 0 + Arguments.of(100, 0, false), // dest 0 + Arguments.of(0, 100, false), // src 0 + Arguments.of(100, 200, true), + Arguments.of(200, 100, false), + Arguments.of(100, 100, true)); + } + + @ParameterizedTest + @MethodSource + void testIsUpToDate(long source, long destination, boolean expected) { + assertThat(ResourceUtils.isUptodate(source, destination)).isEqualTo(expected); + } +}