diff --git a/common/common/src/main/java/io/helidon/build/common/FileUtils.java b/common/common/src/main/java/io/helidon/build/common/FileUtils.java
index 07be4836a..2bb00edaa 100644
--- a/common/common/src/main/java/io/helidon/build/common/FileUtils.java
+++ b/common/common/src/main/java/io/helidon/build/common/FileUtils.java
@@ -103,7 +103,7 @@ public final class FileUtils {
*/
public static Path requiredDirectoryFromProperty(String systemPropertyName, boolean createIfRequired) {
final String path = Requirements.requireNonNull(System.getProperty(systemPropertyName),
- "Required system property %s not set", systemPropertyName);
+ "Required system property %s not set", systemPropertyName);
return requiredDirectory(path, createIfRequired);
}
@@ -1006,7 +1006,16 @@ public static Path resourceAsPath(String path, Class> clazz) throws IllegalArg
* @return extension or {@code null}
*/
public static String fileExt(Path file) {
- String filename = file.getFileName().toString();
+ return fileExt(file.getFileName().toString());
+ }
+
+ /**
+ * Get the file extension for the given file name.
+ *
+ * @param filename file name
+ * @return extension or {@code null}
+ */
+ public static String fileExt(String filename) {
int index = filename.lastIndexOf(".");
return index < 0 ? null : filename.substring(index + 1);
}
diff --git a/maven-plugins/stager-maven-plugin/README.md b/maven-plugins/stager-maven-plugin/README.md
index 4fc0e9658..a3b8351b9 100644
--- a/maven-plugins/stager-maven-plugin/README.md
+++ b/maven-plugins/stager-maven-plugin/README.md
@@ -55,12 +55,33 @@ The above parameters are mapped to user properties of the form `stager.PROPERTY`
+
+
+
+
+
+
+
+
+
+
+ 1.2.3
+
+
+
+
+
+
+
diff --git a/maven-plugins/stager-maven-plugin/etc/spotbugs/exclude.xml b/maven-plugins/stager-maven-plugin/etc/spotbugs/exclude.xml
index dd45acef6..83865a962 100644
--- a/maven-plugins/stager-maven-plugin/etc/spotbugs/exclude.xml
+++ b/maven-plugins/stager-maven-plugin/etc/spotbugs/exclude.xml
@@ -1,7 +1,7 @@
+
+
+
+
diff --git a/maven-plugins/stager-maven-plugin/src/it/projects/unpack-artifact/pom.xml b/maven-plugins/stager-maven-plugin/src/it/projects/unpack-artifact/pom.xml
index 94b33ff04..2ddf9d48c 100644
--- a/maven-plugins/stager-maven-plugin/src/it/projects/unpack-artifact/pom.xml
+++ b/maven-plugins/stager-maven-plugin/src/it/projects/unpack-artifact/pom.xml
@@ -1,6 +1,6 @@
+
+
+ 4.0.0
+ io.helidon.build-tools.stager.tests
+ unpack
+ @project.version@
+ Test Stager Unpack task
+ pom
+
+
+
+
+
+ io.helidon.build-tools
+ helidon-stager-maven-plugin
+ ${project.version}
+
+
+
+
+
+
+
+
+ 3.2.10
+ 3.2.9
+
+
+
+
+
+
+
+
+
+
+
+
+
+ io.helidon.build-tools
+ helidon-stager-maven-plugin
+ ${project.version}
+
+
+
+ stage
+
+
+
+
+
+
+
diff --git a/maven-plugins/stager-maven-plugin/src/it/projects/unpack/postbuild.groovy b/maven-plugins/stager-maven-plugin/src/it/projects/unpack/postbuild.groovy
new file mode 100644
index 000000000..f0a7ec32c
--- /dev/null
+++ b/maven-plugins/stager-maven-plugin/src/it/projects/unpack/postbuild.groovy
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.build.common.test.utils.JUnitLauncher
+import io.helidon.build.maven.stager.ProjectsTestIT
+
+//noinspection GroovyAssignabilityCheck,GrUnresolvedAccess
+JUnitLauncher.builder()
+ .select(ProjectsTestIT.class, "testUnpack", String.class)
+ .parameter("basedir", basedir.getAbsolutePath())
+ .reportsDir(basedir)
+ .outputFile(new File(basedir, "test.log"))
+ .suiteId("stager-unpack-it-test")
+ .suiteDisplayName("Stager Unpack Integration Test")
+ .build()
+ .launch()
diff --git a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/DownloadTask.java b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/DownloadTask.java
index b8af82614..fba218c2c 100644
--- a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/DownloadTask.java
+++ b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/DownloadTask.java
@@ -61,14 +61,24 @@ protected CompletableFuture execBody(StagingContext ctx, Path dir, Map vars) throws IOException {
- download(ctx, dir, vars);
- }
-
- private void download(StagingContext ctx, Path dir, Map vars) throws IOException {
String path = resolveVar(target(), vars);
Path file = dir.resolve(path).normalize();
ensureDirectory(file.getParent());
URL url = new URL(resolveVar(this.url, vars));
+ download(ctx, url, file);
+ }
+
+ /**
+ * Download a file.
+ *
+ * @param ctx staging context
+ * @param url url
+ * @param file target file
+ * @throws IOException if an IO error occurs
+ */
+ static void download(StagingContext ctx, URL url, Path file)
+ throws IOException {
+
try (BufferedInputStream bis = new BufferedInputStream(open(url, ctx))) {
try (OutputStream fos = Files.newOutputStream(file)) {
int n;
@@ -86,19 +96,19 @@ private void download(StagingContext ctx, Path dir, Map vars) th
totalTime = (currentTime - startTime) / 1000;
progressTime = currentTime;
ctx.logInfo("Downloading %s to %s (%s at %s/s)",
- url, path, measuredSize(totalSize), measuredSize(totalSize / totalTime));
+ url, file, measuredSize(totalSize), measuredSize(totalSize / totalTime));
}
}
if (currentTime - progressTime >= 1000) {
totalTime = (currentTime - startTime) / 1000;
}
ctx.logInfo("Downloaded %s to %s (%s at %s/s)",
- url, path, measuredSize(totalSize), measuredSize(totalSize / totalTime));
+ url, file, measuredSize(totalSize), measuredSize(totalSize / totalTime));
}
}
}
- private InputStream open(URL url, StagingContext context) throws IOException {
+ private static InputStream open(URL url, StagingContext context) throws IOException {
NetworkConnection.Builder builder = NetworkConnection.builder().url(url);
int readTimeout = context.readTimeout();
if (readTimeout > 0) {
diff --git a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContext.java b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContext.java
index 79db61541..3a184ffd5 100644
--- a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContext.java
+++ b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2020, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -101,6 +101,17 @@ default Path createTempDirectory(String prefix) throws IOException {
return Files.createTempDirectory(prefix);
}
+ /**
+ * Create a temporary file.
+ *
+ * @param suffix suffix
+ * @return created file
+ * @throws IOException if an IO error occurs
+ */
+ default Path createTempFile(String suffix) throws IOException {
+ return Files.createTempFile(null, suffix);
+ }
+
/**
* Log an info message.
*
diff --git a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContextImpl.java b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContextImpl.java
index fa04cf97e..689c91e0c 100644
--- a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContextImpl.java
+++ b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingContextImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022 Oracle and/or its affiliates.
+ * Copyright (c) 2022, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -80,17 +80,17 @@ final class StagingContextImpl implements StagingContext {
this.archiverManager = Objects.requireNonNull(archiverManager, "archiverManager is null");
this.executor = executor;
this.readTimeout = Optional.ofNullable(propertyResolver.apply(StagingContext.READ_TIMEOUT_PROP))
- .map(Integer::parseInt)
- .orElse(-1);
+ .map(Integer::parseInt)
+ .orElse(-1);
this.connectTimeout = Optional.ofNullable(propertyResolver.apply(StagingContext.CONNECT_TIMEOUT_PROP))
- .map(Integer::parseInt)
- .orElse(-1);
+ .map(Integer::parseInt)
+ .orElse(-1);
this.taskTimeout = Optional.ofNullable(propertyResolver.apply(StagingContext.TASK_TIMEOUT_PROP))
- .map(Integer::parseInt)
- .orElse(-1);
+ .map(Integer::parseInt)
+ .orElse(-1);
this.maxRetries = Optional.ofNullable(propertyResolver.apply(StagingContext.MAX_RETRIES))
- .map(Integer::parseInt)
- .orElse(-1);
+ .map(Integer::parseInt)
+ .orElse(-1);
}
@Override
@@ -110,7 +110,7 @@ public void unpack(Path archive, Path target, String excludes, String includes)
unArchiver.setSourceFile(archiveFile);
unArchiver.setDestDirectory(target.toFile());
if (StringUtils.isNotEmpty(excludes) || StringUtils.isNotEmpty(includes)) {
- IncludeExcludeFileSelector[] selectors = new IncludeExcludeFileSelector[]{
+ IncludeExcludeFileSelector[] selectors = new IncludeExcludeFileSelector[] {
new IncludeExcludeFileSelector()
};
if (StringUtils.isNotEmpty(excludes)) {
@@ -177,6 +177,11 @@ public Path createTempDirectory(String prefix) throws IOException {
return Files.createTempDirectory(outputDir.toPath(), prefix);
}
+ @Override
+ public Path createTempFile(String suffix) throws IOException {
+ return Files.createTempFile(outputDir.toPath(), null, suffix);
+ }
+
@Override
public void logInfo(String msg, Object... args) {
log.info(String.format(msg, args));
diff --git a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingElementFactory.java b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingElementFactory.java
index 5cadd7058..4de35fbf5 100644
--- a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingElementFactory.java
+++ b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/StagingElementFactory.java
@@ -41,6 +41,7 @@ class StagingElementFactory {
FileTask.ELEMENT_NAME,
SymlinkTask.ELEMENT_NAME,
TemplateTask.ELEMENT_NAME,
+ UnpackTask.ELEMENT_NAME,
UnpackArtifactTask.ELEMENT_NAME,
ListFilesTask.ELEMENT_NAME);
@@ -98,6 +99,8 @@ StagingAction createAction(String name,
switch (name) {
case StagingDirectory.ELEMENT_NAME:
return new StagingDirectory(filterChildren(children, StagingAction.class), attrs);
+ case UnpackTask.ELEMENT_NAME:
+ return new UnpackTask(iterators.get(), attrs);
case UnpackArtifactTask.ELEMENT_NAME:
return new UnpackArtifactTask(iterators.get(), attrs);
case CopyArtifactTask.ELEMENT_NAME:
diff --git a/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/UnpackTask.java b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/UnpackTask.java
new file mode 100644
index 000000000..e3e8ea4b7
--- /dev/null
+++ b/maven-plugins/stager-maven-plugin/src/main/java/io/helidon/build/maven/stager/UnpackTask.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2020, 2024 Oracle and/or its affiliates.
+ *
+ * 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 io.helidon.build.maven.stager;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+
+import io.helidon.build.common.Strings;
+
+import static io.helidon.build.common.FileUtils.ensureDirectory;
+import static io.helidon.build.common.FileUtils.fileExt;
+import static io.helidon.build.maven.stager.DownloadTask.download;
+
+/**
+ * Download an unpack to a given target location.
+ */
+final class UnpackTask extends StagingTask {
+
+ static final String ELEMENT_NAME = "unpack";
+
+ private final String ext;
+ private final String url;
+ private final String includes;
+ private final String excludes;
+
+ UnpackTask(ActionIterators iterators, Map attrs) {
+ super(ELEMENT_NAME, null, iterators, attrs);
+ this.url = Strings.requireValid(attrs.get("url"), "url is required");
+ this.ext = Strings.requireValid(Optional.ofNullable(attrs.get("ext"))
+ .orElseGet(() -> fileExt(url)), "ext is required");
+ this.includes = attrs.get("includes");
+ this.excludes = attrs.get("excludes");
+ }
+
+ /**
+ * Get the url.
+ *
+ * @return url, never {@code null}
+ */
+ String url() {
+ return url;
+ }
+
+ /**
+ * Get the excludes.
+ *
+ * @return excludes, may be {@code null}
+ */
+ String excludes() {
+ return excludes;
+ }
+
+ /**
+ * Get the includes.
+ *
+ * @return includes, may be {@code null}
+ */
+ String includes() {
+ return includes;
+ }
+
+ @Override
+ protected CompletableFuture execBody(StagingContext ctx, Path dir, Map vars) {
+ return execBodyWithTimeout(ctx, dir, vars);
+ }
+
+ @Override
+ protected void doExecute(StagingContext ctx, Path dir, Map vars) throws IOException {
+ Path tempFile = ctx.createTempFile("." + ext);
+ URL url = new URL(resolveVar(this.url, vars));
+ download(ctx, url, tempFile);
+
+ String resolvedTarget = resolveVar(target(), vars);
+ Path targetDir = dir.resolve(resolvedTarget).normalize();
+ ctx.logInfo("Unpacking %s to %s", tempFile, targetDir);
+ ensureDirectory(targetDir);
+ ctx.unpack(tempFile, targetDir, excludes, includes);
+ }
+}
diff --git a/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ConfigReaderTest.java b/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ConfigReaderTest.java
index 0c60214c9..ad5197fc0 100644
--- a/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ConfigReaderTest.java
+++ b/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ConfigReaderTest.java
@@ -55,39 +55,39 @@ public void testConverter() throws Exception {
StagingDirectory dir1 = (StagingDirectory) root.tasks().get(0);
assertThat(dir1.target(), is("${project.build.directory}/site"));
List extends StagingAction> dir1Tasks = dir1.tasks();
- assertThat(dir1Tasks.size(), is(6));
+ assertThat(dir1Tasks.size(), is(7));
dir1Tasks.forEach(c -> assertThat(c, is(instanceOf(StagingTasks.class))));
List extends StagingAction> unpackArtifacts = ((StagingTasks) dir1Tasks.get(0)).tasks();
assertThat(unpackArtifacts.size(), is(2));
- UnpackArtifactTask unpack1 = (UnpackArtifactTask) unpackArtifacts.get(0);
- assertThat(unpack1.gav().groupId(), is("io.helidon"));
- assertThat(unpack1.gav().artifactId(), is("helidon-docs"));
- assertThat(unpack1.gav().version(), is("{version}"));
- assertThat(unpack1.excludes(), is("META-INF/**"));
- assertThat(unpack1.includes(), is(nullValue()));
- assertThat(unpack1.target(), is("docs/{version}"));
- assertThat(unpack1.iterators().size(), is(1));
- assertThat(unpack1.iterators().get(0).next().get("version"), is("${docs.1.version}"));
- assertThat(unpack1.iterators().get(0).next().get("version"), is("1.4.3"));
- assertThat(unpack1.iterators().get(0).next().get("version"), is("1.4.2"));
- assertThat(unpack1.iterators().get(0).next().get("version"), is("1.4.1"));
- assertThat(unpack1.iterators().get(0).next().get("version"), is("1.4.0"));
- assertThat(unpack1.iterators().get(0).hasNext(), is(false));
-
- UnpackArtifactTask unpack2 = (UnpackArtifactTask) unpackArtifacts.get(1);
- assertThat(unpack2.gav().groupId(), is("io.helidon"));
- assertThat(unpack2.gav().artifactId(), is("helidon-project"));
- assertThat(unpack2.gav().version(), is("{version}"));
- assertThat(unpack2.gav().classifier(), is("site"));
- assertThat(unpack2.excludes(), is("META-INF/**"));
- assertThat(unpack2.includes(), is(nullValue()));
- assertThat(unpack2.target(), is("docs/{version}"));
- assertThat(unpack2.iterators().size(), is(1));
- assertThat(unpack2.iterators().get(0).next().get("version"), is("${docs.2.version}"));
- assertThat(unpack2.iterators().get(0).hasNext(), is(false));
+ UnpackArtifactTask unpackGAV1 = (UnpackArtifactTask) unpackArtifacts.get(0);
+ assertThat(unpackGAV1.gav().groupId(), is("io.helidon"));
+ assertThat(unpackGAV1.gav().artifactId(), is("helidon-docs"));
+ assertThat(unpackGAV1.gav().version(), is("{version}"));
+ assertThat(unpackGAV1.excludes(), is("META-INF/**"));
+ assertThat(unpackGAV1.includes(), is(nullValue()));
+ assertThat(unpackGAV1.target(), is("docs/{version}"));
+ assertThat(unpackGAV1.iterators().size(), is(1));
+ assertThat(unpackGAV1.iterators().get(0).next().get("version"), is("${docs.1.version}"));
+ assertThat(unpackGAV1.iterators().get(0).next().get("version"), is("1.4.3"));
+ assertThat(unpackGAV1.iterators().get(0).next().get("version"), is("1.4.2"));
+ assertThat(unpackGAV1.iterators().get(0).next().get("version"), is("1.4.1"));
+ assertThat(unpackGAV1.iterators().get(0).next().get("version"), is("1.4.0"));
+ assertThat(unpackGAV1.iterators().get(0).hasNext(), is(false));
+
+ UnpackArtifactTask unpackGAV2 = (UnpackArtifactTask) unpackArtifacts.get(1);
+ assertThat(unpackGAV2.gav().groupId(), is("io.helidon"));
+ assertThat(unpackGAV2.gav().artifactId(), is("helidon-project"));
+ assertThat(unpackGAV2.gav().version(), is("{version}"));
+ assertThat(unpackGAV2.gav().classifier(), is("site"));
+ assertThat(unpackGAV2.excludes(), is("META-INF/**"));
+ assertThat(unpackGAV2.includes(), is(nullValue()));
+ assertThat(unpackGAV2.target(), is("docs/{version}"));
+ assertThat(unpackGAV2.iterators().size(), is(1));
+ assertThat(unpackGAV2.iterators().get(0).next().get("version"), is("${docs.2.version}"));
+ assertThat(unpackGAV2.iterators().get(0).hasNext(), is(false));
List extends StagingAction> symlinks = ((StagingTasks) dir1Tasks.get(1)).tasks();
assertThat(symlinks.size(), is(4));
@@ -315,5 +315,13 @@ public void testConverter() throws Exception {
assertThat(substitution.match(), is("^(?([^/]+/)*)index.html$"));
assertThat(substitution.replace(), is("{path}"));
assertThat(substitution.isRegex(), is(true));
+
+ List extends StagingAction> unpacks = ((StagingTasks) dir1Tasks.get(6)).tasks();
+ assertThat(unpacks.size(), is(1));
+
+ UnpackTask unpack1 = (UnpackTask) unpacks.get(0);
+ assertThat(unpack1.url(), is("https://repo1.maven.org/maven2/io/helidon/helidon-project/3.2.10/helidon-project-3.2.10-site.jar"));
+ assertThat(unpack1.target(), is("3.2.10"));
+ assertThat(unpack1.tasks(), is(empty()));
}
}
diff --git a/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ProjectsTestIT.java b/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ProjectsTestIT.java
index b2bc81ca3..e3e4de6c5 100644
--- a/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ProjectsTestIT.java
+++ b/maven-plugins/stager-maven-plugin/src/test/java/io/helidon/build/maven/stager/ProjectsTestIT.java
@@ -211,6 +211,18 @@ void testUnpackArtifact(String basedir) {
assertThat(docsDir.resolve("2.0.0-RC1"), fileExists());
}
+ @ParameterizedTest
+ @ConfigurationParameterSource("basedir")
+ void testUnpack(String basedir) {
+ Path stageDir = Path.of(basedir).resolve("target/stage");
+ assertThat(stageDir, fileExists());
+
+ Path docsDir = stageDir.resolve("docs");
+ assertThat(docsDir, fileExists());
+ assertThat(docsDir.resolve("3.2.10"), fileExists());
+ assertThat(docsDir.resolve("3.2.9"), fileExists());
+ }
+
private String symlinkTarget(Path file) {
try {
assertThat(file + " is not a symbolic link", Files.isSymbolicLink(file), is(true));
diff --git a/maven-plugins/stager-maven-plugin/src/test/resources/test-config.xml b/maven-plugins/stager-maven-plugin/src/test/resources/test-config.xml
index c9f1e1d4b..43eb140ae 100644
--- a/maven-plugins/stager-maven-plugin/src/test/resources/test-config.xml
+++ b/maven-plugins/stager-maven-plugin/src/test/resources/test-config.xml
@@ -192,5 +192,9 @@
+
+
+