JarUpdaterMojo.java
/*
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.tyrus.plugins.jarupdater;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Mojo(name = "update", defaultPhase = LifecyclePhase.VERIFY, threadSafe = true)
public class JarUpdaterMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", required = true, readonly = true)
MavenProject project;
@Parameter(property = "files")
List<String> files;
@Parameter(property = "dest")
String dest;
@Parameter(property = "jar")
String jar;
@Parameter(property = "ignoreOnError", defaultValue = "false")
String ignoreOnError;
@Parameter(name = "verbose", property = "verbose", defaultValue = "true")
String verboseParam;
@Parameter(property = "skip", name = "skip")
String skip;
File absoluteJar;
List<File> sources;
List<Pattern> skipPatterns;
boolean ignore;
boolean verbose;
@Override
public void execute() throws MojoExecutionException {
ignore = Boolean.parseBoolean(ignoreOnError);
verbose = Boolean.parseBoolean(verboseParam);
try {
prepareSkipPatterns();
if (!skipModule()) {
validateOptions();
updateJar();
}
} catch (Exception e) {
e.printStackTrace();
throw new MojoExecutionException(e);
}
}
private void updateJar() throws IOException, URISyntaxException {
try (FileSystem fs = FileSystems.newFileSystem(absoluteJar.toPath(), (ClassLoader) null)) {
for (File fileToZip : sources) {
if (verbose) {
System.out.append("Adding ").append(fileToZip.getName()).append(" to ")
.append(absoluteJar.getName()).append('/').println(dest);
}
Path pathInZipfile = fs.getPath(dest + "/" + fileToZip.getName());
ensureFolder(pathInZipfile);
Files.copy(fileToZip.toPath(), pathInZipfile, StandardCopyOption.REPLACE_EXISTING);
}
};
}
private void ensureFolder(Path path) throws IOException {
if (path.getParent() != null) {
if (!Files.exists(path.getParent())) {
ensureFolder(path.getParent());
Files.createDirectory(path.getParent());
}
}
}
private void validateOptions() throws MojoExecutionException, IOException {
if (jar == null) {
throw new MojoExecutionException("Jar name not defined");
}
// -----------
absoluteJar = validateJar(jar);
if (dest == null || dest.isEmpty()) {
throw new MojoExecutionException("Destination is mandatory");
}
// -----------
if (files == null) {
throw new MojoExecutionException("Files set is required");
}
sources = new ArrayList<>(files.size());
for (String file : files) {
validateSource(file);
}
}
private void validateSource(String file) throws MojoExecutionException {
File absolute = validateAbsolute(file, ignore);
if (absolute != null) {
if (!absolute.isDirectory()) {
sources.add(absolute);
} else {
listAllFiles(absolute.toPath(), sources);
}
}
}
private File validateJar(String source) throws MojoExecutionException {
File absolute = validateAbsolute(source, false);
if (absolute.isDirectory()) {
throw new MojoExecutionException("Provided source " + source + " is a directory");
}
return absolute;
}
private File validateAbsolute(String source, boolean ignore) throws MojoExecutionException {
File absolute = getAbsolute(source);
if (!absolute.exists()) {
if (!ignore) {
throw new MojoExecutionException("Provided source " + source + " does not exist");
} else {
System.out.println("Warning: Ignoring non-existing " + source);
return null;
}
}
return absolute;
}
private File getAbsolute(String sfile) {
File file = new File(sfile);
if (!file.isAbsolute()) {
file = new File(project.getBasedir(), sfile);
}
return file;
}
private void prepareSkipPatterns() {
if (skip != null) {
String[] skipIdss = skip.split(",");
skipPatterns = new ArrayList<>(skipIdss.length);
for (String skip : skipIdss) {
if (!skip.isEmpty()) {
String pat = skip.trim().replace(".", "\\.").replace("*", ".*");
Pattern pattern = Pattern.compile(pat);
skipPatterns.add(pattern);
}
}
} else {
skipPatterns = Collections.emptyList();
}
}
private boolean skipModule() {
String moduleName = project.getGroupId() + ":" + project.getArtifactId();
for (Pattern pattern : skipPatterns) {
Matcher matcher = pattern.matcher(moduleName);
if (matcher.matches()) {
System.out.append("Skipping module ").println(moduleName);
return true;
}
}
return false;
}
private static void listAllFiles(Path currentPath, List<File> allFiles) throws MojoExecutionException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(currentPath)) {
for (Path entry : stream) {
if (Files.isDirectory(entry)) {
listAllFiles(entry, allFiles);
} else {
allFiles.add(entry.toFile());
}
}
} catch (IOException ioe) {
throw new MojoExecutionException(ioe);
}
}
}