Improve log and leverage the new infrastructure in Maven (#1164)

This commit is contained in:
Guillaume Nodet
2024-10-15 18:15:15 +02:00
committed by GitHub
parent 27258c0e54
commit a032544164
38 changed files with 134 additions and 2164 deletions

View File

@@ -22,7 +22,6 @@ import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
@@ -31,7 +30,6 @@ import org.apache.commons.cli.ParseException;
import org.apache.maven.cling.invoker.mvn.CommonsCliMavenOptions;
import org.codehaus.plexus.interpolation.BasicInterpolator;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.mvndaemon.mvnd.common.Environment;
import static org.apache.maven.cling.invoker.Utils.createInterpolator;
@@ -49,15 +47,6 @@ public class CommonsCliDaemonMavenOptions extends CommonsCliMavenOptions impleme
return this.cliManager.getOptions();
}
@Override
public Optional<Boolean> rawStreams() {
if (commandLine.hasOption(CLIManager.RAW_STREAMS)
|| Environment.MVND_RAW_STREAMS.asOptional().isPresent()) {
return Optional.of(Boolean.TRUE);
}
return Optional.empty();
}
private static CommonsCliDaemonMavenOptions interpolate(
CommonsCliDaemonMavenOptions options, Collection<Map<String, String>> properties) {
try {

View File

@@ -18,20 +18,25 @@
*/
package org.apache.maven.cli;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.apache.maven.logging.BuildEventListener;
/**
* Simple interface to bridge maven 3.9.x and 4.0.x CLI
*/
public interface DaemonCli extends AutoCloseable {
public interface DaemonCli {
int main(
List<String> args,
String workingDir,
String projectDir,
Map<String, String> env,
BuildEventListener buildEventListener)
BuildEventListener buildEventListener,
InputStream in,
OutputStream out,
OutputStream err)
throws Exception;
}

View File

@@ -18,9 +18,8 @@
*/
package org.apache.maven.cli;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
@@ -29,30 +28,12 @@ import java.util.function.Supplier;
import org.apache.maven.api.cli.ParserRequest;
import org.apache.maven.cling.invoker.ProtoLogger;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.jline.MessageUtils;
import org.apache.maven.logging.BuildEventListener;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.jline.terminal.Terminal;
import org.jline.terminal.impl.ExternalTerminal;
import org.mvndaemon.mvnd.cli.EnvHelper;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
public class DaemonMavenCling implements DaemonCli {
private final DaemonMavenParser parser;
private final DaemonMavenInvoker invoker;
public DaemonMavenCling() {
this.parser = new DaemonMavenParser();
this.invoker = new DaemonMavenInvoker(ProtoLookup.builder()
.addMapping(
ClassWorld.class, ((ClassRealm) Thread.currentThread().getContextClassLoader()).getWorld())
.build());
}
@Override
public void close() throws Exception {
invoker.close();
}
@Override
public int main(
@@ -60,25 +41,32 @@ public class DaemonMavenCling implements DaemonCli {
String workingDir,
String projectDir,
Map<String, String> env,
BuildEventListener buildEventListener)
BuildEventListener buildEventListener,
InputStream in,
OutputStream out,
OutputStream err)
throws Exception {
Terminal terminal = new ExternalTerminal(
"Maven",
"ansi",
new ByteArrayInputStream(new byte[0]),
new ByteArrayOutputStream(),
StandardCharsets.UTF_8);
MessageUtils.systemInstall(terminal);
EnvHelper.environment(workingDir, env);
System.setProperty("maven.multiModuleProjectDirectory", projectDir);
return invoker.invoke(parser.parse(ParserRequest.builder(
"mvnd", "Maven Daemon", args, new ProtoLogger(), new DaemonMessageBuilderFactory())
.cwd(Paths.get(workingDir))
.lookup(ProtoLookup.builder()
.addMapping(Environment.class, () -> env)
.addMapping(BuildEventListener.class, buildEventListener)
.build())
.build()));
try (DaemonMavenInvoker invoker = new DaemonMavenInvoker(ProtoLookup.builder()
.addMapping(
ClassWorld.class, ((ClassRealm) Thread.currentThread().getContextClassLoader()).getWorld())
.addMapping(BuildEventListener.class, buildEventListener)
.build())) {
DaemonMavenParser parser = new DaemonMavenParser();
return invoker.invoke(parser.parse(ParserRequest.builder(
"mvnd", "Maven Daemon", args, new ProtoLogger(), new DaemonMessageBuilderFactory())
.cwd(Paths.get(workingDir))
.in(in)
.out(out)
.err(err)
.lookup(ProtoLookup.builder()
.addMapping(Environment.class, () -> env)
.addMapping(BuildEventListener.class, buildEventListener)
.build())
.build()));
}
}
/**

View File

@@ -18,23 +18,24 @@
*/
package org.apache.maven.cli;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import org.apache.maven.api.cli.Options;
import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cli.event.ExecutionEventLogger;
import org.apache.maven.api.services.MavenException;
import org.apache.maven.cling.invoker.ContainerCapsuleFactory;
import org.apache.maven.cling.invoker.ProtoLookup;
import org.apache.maven.cling.invoker.mvn.resident.DefaultResidentMavenInvoker;
import org.apache.maven.execution.ExecutionListener;
import org.apache.maven.jline.MessageUtils;
import org.eclipse.aether.transfer.TransferListener;
import org.apache.maven.logging.BuildEventListener;
import org.apache.maven.logging.LoggingOutputStream;
import org.jline.terminal.Terminal;
import org.jline.terminal.impl.ExternalTerminal;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.logging.slf4j.MvndSimpleLogger;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.mvndaemon.mvnd.logging.smart.LoggingExecutionListener;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
import org.mvndaemon.mvnd.transfer.DaemonMavenTransferListener;
import org.slf4j.spi.LocationAwareLogger;
public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
public DaemonMavenInvoker(ProtoLookup protoLookup) {
@@ -44,25 +45,54 @@ public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
@Override
protected void configureLogging(LocalContext context) throws Exception {
super.configureLogging(context);
}
DaemonMavenOptions options = (DaemonMavenOptions) context.invokerRequest.options();
if (options.logFile().isEmpty() && !options.rawStreams().orElse(false)) {
MvndSimpleLogger stdout = (MvndSimpleLogger) context.loggerFactory.getLogger("stdout");
MvndSimpleLogger stderr = (MvndSimpleLogger) context.loggerFactory.getLogger("stderr");
stdout.setLogLevel(LocationAwareLogger.INFO_INT);
stderr.setLogLevel(LocationAwareLogger.INFO_INT);
System.setOut(new LoggingOutputStream(s -> stdout.info("[stdout] " + s)).printStream());
System.setErr(new LoggingOutputStream(s -> stderr.warn("[stderr] " + s)).printStream());
@Override
protected Terminal createTerminal(LocalContext context) {
try {
Terminal terminal = new ExternalTerminal(
"Maven",
"ansi",
context.invokerRequest.in().get(),
context.invokerRequest.out().get(),
StandardCharsets.UTF_8);
doConfigureWithTerminal(context, terminal);
// If raw-streams options has been set, we need to decorate to push back to the client
if (context.invokerRequest.options().rawStreams().orElse(false)) {
BuildEventListener bel = determineBuildEventListener(context);
OutputStream out = context.invokerRequest.out().orElse(null);
System.setOut(out != null ? printStream(out) : new LoggingOutputStream(bel::log).printStream());
OutputStream err = context.invokerRequest.err().orElse(null);
System.setErr(err != null ? printStream(err) : new LoggingOutputStream(bel::log).printStream());
}
return terminal;
} catch (IOException e) {
throw new MavenException("Error creating terminal", e);
}
}
private PrintStream printStream(OutputStream outputStream) {
if (outputStream instanceof LoggingOutputStream los) {
return los.printStream();
} else if (outputStream instanceof PrintStream ps) {
return ps;
} else {
return new PrintStream(outputStream);
}
}
@Override
protected org.apache.maven.logging.BuildEventListener doDetermineBuildEventListener(LocalContext context) {
return protoLookup.lookup(BuildEventListener.class);
}
@Override
protected void helpOrVersionAndMayExit(LocalContext context) throws Exception {
MavenInvokerRequest<MavenOptions> invokerRequest = context.invokerRequest;
BuildEventListener buildEventListener =
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class);
if (invokerRequest.options().help().isPresent()) {
// TODO: ugly, clenup
// TODO: ugly, cleanup
buildEventListener.log(
MvndHelpFormatter.displayHelp((CommonsCliDaemonMavenOptions) context.invokerRequest.options()));
throw new ExitException(0);
@@ -95,29 +125,6 @@ public class DaemonMavenInvoker extends DefaultResidentMavenInvoker {
return new DaemonPlexusContainerCapsuleFactory();
}
@Override
protected ExecutionListener determineExecutionListener(LocalContext context) {
if (context.lookup != null) {
LoggingExecutionListener listener = context.lookup.lookup(LoggingExecutionListener.class);
ExecutionEventLogger executionEventLogger =
new ExecutionEventLogger(context.invokerRequest.messageBuilderFactory());
listener.init(
context.eventSpyDispatcher.chainListener(executionEventLogger),
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class));
return listener;
} else {
// this branch happens in "early" step of container capsule to load extensions
return super.determineExecutionListener(context);
}
}
@Override
protected TransferListener determineTransferListener(LocalContext context, boolean noTransferProgress) {
return new DaemonMavenTransferListener(
context.invokerRequest.parserRequest().lookup().lookup(BuildEventListener.class),
super.determineTransferListener(context, noTransferProgress));
}
@Override
protected int doExecute(LocalContext context) throws Exception {
context.logger.info(MessageUtils.builder()

View File

@@ -20,15 +20,10 @@ package org.apache.maven.cli;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import org.apache.maven.api.cli.mvn.MavenOptions;
public interface DaemonMavenOptions extends MavenOptions {
/**
* Should use raw-streams to communicate with daemon.
*/
Optional<Boolean> rawStreams();
@Override
DaemonMavenOptions interpolate(Collection<Map<String, String>> properties);

View File

@@ -27,19 +27,11 @@ import org.apache.maven.api.cli.mvn.MavenInvokerRequest;
import org.apache.maven.api.cli.mvn.MavenOptions;
import org.apache.maven.cling.invoker.PlexusContainerCapsuleFactory;
import org.apache.maven.cling.invoker.mvn.resident.DefaultResidentMavenInvoker;
import org.codehaus.plexus.logging.LoggerManager;
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.logging.internal.Slf4jLoggerManager;
public class DaemonPlexusContainerCapsuleFactory
extends PlexusContainerCapsuleFactory<
MavenOptions, MavenInvokerRequest<MavenOptions>, DefaultResidentMavenInvoker.LocalContext> {
private final Slf4jLoggerManager slf4jLoggerManager = new Slf4jLoggerManager();
@Override
protected LoggerManager createLoggerManager() {
return slf4jLoggerManager;
}
@Override
protected List<Path> parseExtClasspath(DefaultResidentMavenInvoker.LocalContext context) throws Exception {

View File

@@ -29,6 +29,7 @@ import java.util.regex.Pattern;
import io.takari.maven.builder.smart.DependencyGraph;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.logging.BuildEventListener;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.transfer.TransferEvent;
@@ -37,12 +38,11 @@ import org.eclipse.aether.transfer.TransferEvent.RequestType;
import org.mvndaemon.mvnd.common.Message;
import org.mvndaemon.mvnd.common.Message.BuildException;
import org.mvndaemon.mvnd.common.Message.BuildStarted;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
/**
* Sends events back to the client.
*/
public class ClientDispatcher extends BuildEventListener {
public class ClientDispatcher implements BuildEventListener {
private final Collection<Message> queue;
private static final Pattern TRAILING_EOLS_PATTERN = Pattern.compile("[\r\n]+$");

View File

@@ -52,6 +52,9 @@ import java.util.stream.Collectors;
import io.takari.maven.builder.smart.SmartBuilder;
import org.apache.maven.cli.DaemonCli;
import org.apache.maven.logging.BuildEventListener;
import org.apache.maven.logging.LoggingOutputStream;
import org.apache.maven.logging.ProjectBuildLogAppender;
import org.mvndaemon.mvnd.common.DaemonConnection;
import org.mvndaemon.mvnd.common.DaemonException;
import org.mvndaemon.mvnd.common.DaemonExpirationStatus;
@@ -67,9 +70,6 @@ import org.mvndaemon.mvnd.common.SignalHelper;
import org.mvndaemon.mvnd.common.SocketFamily;
import org.mvndaemon.mvnd.daemon.DaemonExpiration.DaemonExpirationResult;
import org.mvndaemon.mvnd.daemon.DaemonExpiration.DaemonExpirationStrategy;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.mvndaemon.mvnd.logging.smart.LoggingOutputStream;
import org.mvndaemon.mvnd.logging.smart.ProjectBuildLogAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -187,11 +187,7 @@ public class Server implements AutoCloseable, Runnable {
try {
registry.close();
} finally {
try {
socket.close();
} finally {
cli.close();
}
socket.close();
}
}
}
@@ -509,7 +505,7 @@ public class Server implements AutoCloseable, Runnable {
final BuildEventListener buildEventListener = new ClientDispatcher(sendQueue);
final DaemonInputStream daemonInputStream =
new DaemonInputStream(projectId -> sendQueue.add(Message.requestInput(projectId)));
try (ProjectBuildLogAppender logAppender = new ProjectBuildLogAppender(buildEventListener)) {
try {
LOGGER.info("Executing request");
@@ -610,15 +606,17 @@ public class Server implements AutoCloseable, Runnable {
}
}
});
System.setIn(daemonInputStream);
System.setOut(new LoggingOutputStream(s -> sendQueue.add(Message.out(s))).printStream());
System.setErr(new LoggingOutputStream(s -> sendQueue.add(Message.err(s))).printStream());
LoggingOutputStream output = new LoggingOutputStream(s -> sendQueue.add(Message.out(s)));
LoggingOutputStream error = new LoggingOutputStream(s -> sendQueue.add(Message.err(s)));
int exitCode = cli.main(
buildRequest.getArgs(),
buildRequest.getWorkingDir(),
buildRequest.getProjectDir(),
buildRequest.getEnv(),
buildEventListener);
buildEventListener,
daemonInputStream,
output,
error);
LOGGER.info("Build finished, finishing message dispatch");
buildEventListener.finish(exitCode);
} catch (Throwable t) {

View File

@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.apache.maven.logging.ProjectBuildLogAppender;
import org.codehaus.plexus.components.interactivity.AbstractInputHandler;
import org.codehaus.plexus.components.interactivity.InputHandler;
import org.codehaus.plexus.components.interactivity.OutputHandler;
@@ -35,7 +36,6 @@ import org.eclipse.sisu.Priority;
import org.eclipse.sisu.Typed;
import org.mvndaemon.mvnd.common.Message;
import org.mvndaemon.mvnd.daemon.Connection;
import org.mvndaemon.mvnd.logging.smart.ProjectBuildLogAppender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -1,72 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.mvndaemon.mvnd.transfer;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferListener;
import org.mvndaemon.mvnd.logging.smart.BuildEventListener;
import org.mvndaemon.mvnd.logging.smart.ProjectBuildLogAppender;
public class DaemonMavenTransferListener implements TransferListener {
private final BuildEventListener dispatcher;
private final TransferListener delegate;
public DaemonMavenTransferListener(BuildEventListener dispatcher, TransferListener delegate) {
this.dispatcher = dispatcher;
this.delegate = delegate;
}
@Override
public void transferInitiated(TransferEvent event) throws TransferCancelledException {
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
delegate.transferInitiated(event);
}
@Override
public void transferStarted(TransferEvent event) throws TransferCancelledException {
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
delegate.transferStarted(event);
}
@Override
public void transferProgressed(TransferEvent event) throws TransferCancelledException {
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
delegate.transferProgressed(event);
}
@Override
public void transferCorrupted(TransferEvent event) throws TransferCancelledException {
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
delegate.transferCorrupted(event);
}
@Override
public void transferSucceeded(TransferEvent event) {
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
delegate.transferSucceeded(event);
}
@Override
public void transferFailed(TransferEvent event) {
dispatcher.transfer(ProjectBuildLogAppender.getProjectId(), event);
delegate.transferFailed(event);
}
}