From ee8523cd78a71ae9e35ba4e445e3e7a430c47da7 Mon Sep 17 00:00:00 2001 From: Sylwester Lachiewicz Date: Mon, 21 Sep 2026 15:24:42 +0200 Subject: [PATCH] Run the "invoker" Maven executor through maven-executor maven-invoker is deprecated (apache/maven-invoker#164). The executor keeps its "invoker" id and behaviour: same options, the additional arguments split the way the forked executor splits them, the build's output on the plugin's logger. --- maven-release-manager/pom.xml | 6 +- .../release/exec/InvokerMavenExecutor.java | 220 +++++++----------- 2 files changed, 87 insertions(+), 139 deletions(-) diff --git a/maven-release-manager/pom.xml b/maven-release-manager/pom.xml index 2b256307..8f737c30 100644 --- a/maven-release-manager/pom.xml +++ b/maven-release-manager/pom.xml @@ -112,9 +112,9 @@ under the License. - org.apache.maven.shared - maven-invoker - 3.3.0 + org.apache.maven.executor + maven-executor + 1.0.0 org.apache.commons diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java index 736ad877..4031f11d 100644 --- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java +++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/exec/InvokerMavenExecutor.java @@ -22,33 +22,37 @@ import javax.inject.Named; import javax.inject.Singleton; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.function.Consumer; +import org.apache.maven.executor.ExecutorException; +import org.apache.maven.executor.ExecutorRequest; +import org.apache.maven.executor.ExecutorResult; +import org.apache.maven.executor.forked.ForkedMavenExecutor; import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer; -import org.apache.maven.shared.invoker.DefaultInvocationRequest; -import org.apache.maven.shared.invoker.DefaultInvoker; -import org.apache.maven.shared.invoker.InvocationRequest; -import org.apache.maven.shared.invoker.InvocationResult; -import org.apache.maven.shared.invoker.Invoker; -import org.apache.maven.shared.invoker.InvokerLogger; -import org.apache.maven.shared.invoker.MavenInvocationException; import org.apache.maven.shared.release.ReleaseResult; import org.apache.maven.shared.release.env.ReleaseEnvironment; import org.apache.maven.shared.release.util.MavenCrypto; -import org.slf4j.Logger; +import org.codehaus.plexus.util.cli.CommandLineUtils; /** - * Fork Maven using the maven-invoker shared library. + * Fork Maven through maven-executor, on the Maven installation of the release environment. + * + * @author Brett Porter */ @Singleton @Named("invoker") public class InvokerMavenExecutor extends AbstractMavenExecutor { + @Inject public InvokerMavenExecutor(MavenCrypto mavenCrypto) { super(mavenCrypto); @@ -64,30 +68,28 @@ public void executeGoals( String pomFileName, ReleaseResult result) throws MavenExecutorException { - InvokerLogger bridge = getInvokerLogger(); - - Invoker invoker = new DefaultInvoker() - .setMavenHome(releaseEnvironment.getMavenHome()) - .setLocalRepositoryDirectory(releaseEnvironment.getLocalRepositoryDirectory()) - .setLogger(bridge); - - InvocationRequest req = new DefaultInvocationRequest() - .setDebug(getLogger().isDebugEnabled()) - .setBaseDirectory(workingDirectory) - // fix for MRELEASE-1105 - // .addShellEnvironment( "MAVEN_DEBUG_OPTS", "" ) - .setBatchMode(!interactive) - .setJavaHome(releaseEnvironment.getJavaHome()) - .setOutputHandler(getLogger()::info) - .setErrorHandler(getLogger()::error); - - // for interactive mode we need some inputs stream - if (interactive) { - req.setInputStream(System.in); + if (releaseEnvironment.getMavenHome() == null) { + throw new MavenExecutorException( + "The release environment does not name a Maven home.", new IllegalStateException("mavenHome")); } + List arguments = new ArrayList<>(); + if (getLogger().isDebugEnabled()) { + arguments.add("-X"); + } + if (!interactive) { + arguments.add("-B"); + } + if (releaseEnvironment.getLocalRepositoryDirectory() != null) { + arguments.add("-Dmaven.repo.local=" + + releaseEnvironment.getLocalRepositoryDirectory().getAbsolutePath()); + } if (pomFileName != null) { - req.setPomFileName(pomFileName); + getLogger() + .debug("Specified POM file is not named 'pom.xml'. " + + "Using the '-f' command-line option to accommodate non-standard filename..."); + arguments.add("-f"); + arguments.add(pomFileName); } File settingsFile = null; @@ -100,36 +102,46 @@ public void executeGoals( try (Writer fileWriter = Files.newBufferedWriter(settingsFile.toPath(), StandardCharsets.UTF_8)) { writer.write(fileWriter, encryptSettings(releaseEnvironment.getSettings())); } - req.setUserSettingsFile(settingsFile); + arguments.add("-s"); + arguments.add(settingsFile.getAbsolutePath()); } catch (IOException e) { throw new MavenExecutorException("Could not create temporary file for release settings.xml", e); } } try { - List targetGoals = new ArrayList<>(goals); - + arguments.addAll(goals); if (additionalArguments != null && !additionalArguments.isEmpty()) { - // additionalArguments will be parsed be MavenInvoker - targetGoals.add(additionalArguments); + try { + arguments.addAll(Arrays.asList(CommandLineUtils.translateCommandline(additionalArguments))); + } catch (Exception e) { + throw new MavenExecutorException("Cannot parse the additional arguments.", e); + } } - req.setGoals(targetGoals); - - try { - InvocationResult invocationResult = invoker.execute(req); - - if (invocationResult.getExecutionException() != null) { - throw new MavenExecutorException( - "Error executing Maven.", invocationResult.getExecutionException()); - } + ExecutorRequest.Builder request = ExecutorRequest.mavenBuilder() + .cwd(workingDirectory.toPath()) + .arguments(arguments) + .stdOut(new LineOutputStream(getLogger()::info)) + .stdErr(new LineOutputStream(getLogger()::error)); + if (releaseEnvironment.getJavaHome() != null) { + request.environmentVariable( + "JAVA_HOME", releaseEnvironment.getJavaHome().getAbsolutePath()); + } + // for interactive mode we need some inputs stream + if (interactive) { + request.stdIn(System.in); + } - if (invocationResult.getExitCode() != 0) { - throw new MavenExecutorException( - "Maven execution failed, exit code: " + invocationResult.getExitCode(), - invocationResult.getExitCode()); + getLogger().debug("Executing: mvn " + String.join(" ", arguments)); + try (ForkedMavenExecutor executor = + new ForkedMavenExecutor(releaseEnvironment.getMavenHome().toPath())) { + ExecutorResult executorResult = executor.execute(request.build()); + if (!executorResult.success()) { + int exitCode = executorResult.exitCode().orElse(-1); + throw new MavenExecutorException("Maven execution failed, exit code: " + exitCode, exitCode); } - } catch (MavenInvocationException e) { + } catch (ExecutorException e) { throw new MavenExecutorException("Failed to invoke Maven build.", e); } } finally { @@ -140,107 +152,43 @@ public void executeGoals( } /** - *

getInvokerLogger.

- * - * @return a {@link org.apache.maven.shared.invoker.InvokerLogger} object + * Hands the build's output to the logger one line at a time. */ - protected InvokerLogger getInvokerLogger() { - return new LoggerBridge(getLogger()); - } - - private static final class LoggerBridge implements InvokerLogger { - - private final Logger logger; - - LoggerBridge(Logger logger) { - this.logger = logger; - } - - @Override - public void debug(String message, Throwable error) { - logger.debug(message, error); - } - - @Override - public void debug(String message) { - logger.debug(message); - } - - @Override - public void error(String message, Throwable error) { - logger.error(message, error); - } - - @Override - public void error(String message) { - logger.error(message); - } - - @Override - public void fatalError(String message, Throwable error) { - logger.error(message, error); - } + private static final class LineOutputStream extends OutputStream { + private final Consumer lines; - @Override - public void fatalError(String message) { - logger.error(message); - } + private final ByteArrayOutputStream line = new ByteArrayOutputStream(); - @Override - public int getThreshold() { - return InvokerLogger.DEBUG; + LineOutputStream(Consumer lines) { + this.lines = lines; } @Override - public void info(String message, Throwable error) { - logger.info(message, error); - } - - @Override - public void info(String message) { - logger.info(message); - } - - @Override - public boolean isDebugEnabled() { - return logger.isDebugEnabled(); - } - - @Override - public boolean isErrorEnabled() { - return logger.isErrorEnabled(); - } - - @Override - public boolean isFatalErrorEnabled() { - return logger.isErrorEnabled(); - } - - @Override - public boolean isInfoEnabled() { - return logger.isInfoEnabled(); - } - - @Override - public boolean isWarnEnabled() { - return logger.isWarnEnabled(); + public void write(int b) { + if (b == '\n') { + flushLine(); + } else if (b != '\r') { + line.write(b); + } } @Override - public void setThreshold(int level) { - // NOTE: - // logger.setThreshold( level ) - // is not supported in plexus-container-default:1.0-alpha-9 as used in Maven 2.x + public void write(byte[] b, int off, int len) { + for (int i = off; i < off + len; i++) { + write(b[i]); + } } @Override - public void warn(String message, Throwable error) { - logger.warn(message, error); + public void close() { + if (line.size() > 0) { + flushLine(); + } } - @Override - public void warn(String message) { - logger.warn(message); + private void flushLine() { + lines.accept(new String(line.toByteArray(), StandardCharsets.UTF_8)); + line.reset(); } } }