Boot JLine in async mode

This commit is contained in:
Guillaume Nodet 2024-04-02 10:30:47 +02:00
parent 691e3780e6
commit a6f52774eb
4 changed files with 319 additions and 24 deletions

View File

@ -58,9 +58,9 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
private final MessageBuilderFactory messageBuilderFactory; private final MessageBuilderFactory messageBuilderFactory;
private final Logger logger; private final Logger logger;
private final int terminalWidth; private int terminalWidth;
private final int lineLength; private int lineLength;
private final int maxProjectNameLength; private int maxProjectNameLength;
private int totalProjects; private int totalProjects;
private volatile int currentVisitedProjectCount; private volatile int currentVisitedProjectCount;
@ -69,17 +69,13 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
} }
public ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger logger) { public ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger logger) {
this(messageBuilderFactory, logger, messageBuilderFactory.getTerminalWidth()); this(messageBuilderFactory, logger, -1);
} }
public ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger logger, int terminalWidth) { public ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger logger, int terminalWidth) {
this.logger = Objects.requireNonNull(logger, "logger cannot be null"); this.logger = Objects.requireNonNull(logger, "logger cannot be null");
this.messageBuilderFactory = messageBuilderFactory; this.messageBuilderFactory = messageBuilderFactory;
this.terminalWidth = Math.min( this.terminalWidth = terminalWidth;
MAX_TERMINAL_WIDTH,
Math.max(terminalWidth < 0 ? DEFAULT_TERMINAL_WIDTH : terminalWidth, MIN_TERMINAL_WIDTH));
this.lineLength = this.terminalWidth - MAX_LOG_PREFIX_SIZE;
this.maxProjectNameLength = this.lineLength - PROJECT_STATUS_SUFFIX_SIZE;
} }
private static String chars(char c, int count) { private static String chars(char c, int count) {
@ -100,9 +96,23 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
logger.info(builder().strong(msg).toString()); logger.info(builder().strong(msg).toString());
} }
private void init() {
if (maxProjectNameLength == 0) {
if (terminalWidth < 0) {
terminalWidth = messageBuilderFactory.getTerminalWidth();
}
terminalWidth = Math.min(
MAX_TERMINAL_WIDTH,
Math.max(terminalWidth < 0 ? DEFAULT_TERMINAL_WIDTH : terminalWidth, MIN_TERMINAL_WIDTH));
lineLength = terminalWidth - MAX_LOG_PREFIX_SIZE;
maxProjectNameLength = lineLength - PROJECT_STATUS_SUFFIX_SIZE;
}
}
@Override @Override
public void projectDiscoveryStarted(ExecutionEvent event) { public void projectDiscoveryStarted(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
logger.info("Scanning for projects..."); logger.info("Scanning for projects...");
} }
} }
@ -110,6 +120,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void sessionStarted(ExecutionEvent event) { public void sessionStarted(ExecutionEvent event) {
if (logger.isInfoEnabled() && event.getSession().getProjects().size() > 1) { if (logger.isInfoEnabled() && event.getSession().getProjects().size() > 1) {
init();
infoLine('-'); infoLine('-');
infoMain("Reactor Build Order:"); infoMain("Reactor Build Order:");
@ -136,6 +147,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void sessionEnded(ExecutionEvent event) { public void sessionEnded(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
if (event.getSession().getProjects().size() > 1) { if (event.getSession().getProjects().size() > 1) {
logReactorSummary(event.getSession()); logReactorSummary(event.getSession());
} }
@ -280,6 +292,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void projectSkipped(ExecutionEvent event) { public void projectSkipped(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
logger.info(""); logger.info("");
infoLine('-'); infoLine('-');
@ -293,6 +306,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void projectStarted(ExecutionEvent event) { public void projectStarted(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
MavenProject project = event.getProject(); MavenProject project = event.getProject();
logger.info(""); logger.info("");
@ -354,6 +368,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void mojoSkipped(ExecutionEvent event) { public void mojoSkipped(ExecutionEvent event) {
if (logger.isWarnEnabled()) { if (logger.isWarnEnabled()) {
init();
logger.warn( logger.warn(
"Goal '{}' requires online mode for execution but Maven is currently offline, skipping", "Goal '{}' requires online mode for execution but Maven is currently offline, skipping",
event.getMojoExecution().getGoal()); event.getMojoExecution().getGoal());
@ -366,6 +381,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void mojoStarted(ExecutionEvent event) { public void mojoStarted(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
logger.info(""); logger.info("");
MessageBuilder buffer = builder().strong("--- "); MessageBuilder buffer = builder().strong("--- ");
@ -386,6 +402,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void forkStarted(ExecutionEvent event) { public void forkStarted(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
logger.info(""); logger.info("");
MessageBuilder buffer = builder().strong(">>> "); MessageBuilder buffer = builder().strong(">>> ");
@ -408,6 +425,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
@Override @Override
public void forkSucceeded(ExecutionEvent event) { public void forkSucceeded(ExecutionEvent event) {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
init();
logger.info(""); logger.info("");
MessageBuilder buffer = builder().strong("<<< "); MessageBuilder buffer = builder().strong("<<< ");
@ -460,6 +478,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener {
public void forkedProjectStarted(ExecutionEvent event) { public void forkedProjectStarted(ExecutionEvent event) {
if (logger.isInfoEnabled() if (logger.isInfoEnabled()
&& event.getMojoExecution().getForkedExecutions().size() > 1) { && event.getMojoExecution().getForkedExecutions().size() > 1) {
init();
logger.info(""); logger.info("");
infoLine('>'); infoLine('>');

View File

@ -0,0 +1,273 @@
/*
* 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.apache.maven.jline;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.IntSupplier;
import org.apache.maven.api.services.MavenException;
import org.jline.terminal.Attributes;
import org.jline.terminal.Cursor;
import org.jline.terminal.MouseEvent;
import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
import org.jline.terminal.spi.SystemStream;
import org.jline.terminal.spi.TerminalExt;
import org.jline.terminal.spi.TerminalProvider;
import org.jline.utils.ColorPalette;
import org.jline.utils.InfoCmp;
import org.jline.utils.NonBlockingReader;
public class FastTerminal implements TerminalExt {
final Future<Terminal> terminal;
public FastTerminal(Callable<Terminal> builder, Consumer<Terminal> consumer) {
ExecutorService executor = Executors.newSingleThreadExecutor();
terminal = executor.submit(() -> {
try {
Terminal terminal = builder.call();
consumer.accept(terminal);
return terminal;
} catch (Exception e) {
throw new MavenException(e);
} finally {
executor.shutdown();
}
});
}
public TerminalExt getTerminal() {
try {
return (TerminalExt) terminal.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String getName() {
return getTerminal().getName();
}
@Override
public SignalHandler handle(Signal signal, SignalHandler signalHandler) {
return getTerminal().handle(signal, signalHandler);
}
@Override
public void raise(Signal signal) {
getTerminal().raise(signal);
}
@Override
public NonBlockingReader reader() {
return getTerminal().reader();
}
@Override
public PrintWriter writer() {
return getTerminal().writer();
}
@Override
public Charset encoding() {
return getTerminal().encoding();
}
@Override
public InputStream input() {
return getTerminal().input();
}
@Override
public OutputStream output() {
return getTerminal().output();
}
@Override
public boolean canPauseResume() {
return getTerminal().canPauseResume();
}
@Override
public void pause() {
getTerminal().pause();
}
@Override
public void pause(boolean b) throws InterruptedException {
getTerminal().pause(b);
}
@Override
public void resume() {
getTerminal().resume();
}
@Override
public boolean paused() {
return getTerminal().paused();
}
@Override
public Attributes enterRawMode() {
return getTerminal().enterRawMode();
}
@Override
public boolean echo() {
return getTerminal().echo();
}
@Override
public boolean echo(boolean b) {
return getTerminal().echo(b);
}
@Override
public Attributes getAttributes() {
return getTerminal().getAttributes();
}
@Override
public void setAttributes(Attributes attributes) {
getTerminal().setAttributes(attributes);
}
@Override
public Size getSize() {
return getTerminal().getSize();
}
@Override
public void setSize(Size size) {
getTerminal().setSize(size);
}
@Override
public int getWidth() {
return getTerminal().getWidth();
}
@Override
public int getHeight() {
return getTerminal().getHeight();
}
@Override
public Size getBufferSize() {
return getTerminal().getBufferSize();
}
@Override
public void flush() {
getTerminal().flush();
}
@Override
public String getType() {
return getTerminal().getType();
}
@Override
public boolean puts(InfoCmp.Capability capability, Object... objects) {
return getTerminal().puts(capability, objects);
}
@Override
public boolean getBooleanCapability(InfoCmp.Capability capability) {
return getTerminal().getBooleanCapability(capability);
}
@Override
public Integer getNumericCapability(InfoCmp.Capability capability) {
return getTerminal().getNumericCapability(capability);
}
@Override
public String getStringCapability(InfoCmp.Capability capability) {
return getTerminal().getStringCapability(capability);
}
@Override
public Cursor getCursorPosition(IntConsumer intConsumer) {
return getTerminal().getCursorPosition(intConsumer);
}
@Override
public boolean hasMouseSupport() {
return getTerminal().hasMouseSupport();
}
@Override
public boolean trackMouse(MouseTracking mouseTracking) {
return getTerminal().trackMouse(mouseTracking);
}
@Override
public MouseEvent readMouseEvent() {
return getTerminal().readMouseEvent();
}
@Override
public MouseEvent readMouseEvent(IntSupplier intSupplier) {
return getTerminal().readMouseEvent(intSupplier);
}
@Override
public boolean hasFocusSupport() {
return getTerminal().hasFocusSupport();
}
@Override
public boolean trackFocus(boolean b) {
return getTerminal().trackFocus(b);
}
@Override
public ColorPalette getPalette() {
return getTerminal().getPalette();
}
@Override
public void close() throws IOException {
getTerminal().close();
}
@Override
public TerminalProvider getProvider() {
return getTerminal().getProvider();
}
@Override
public SystemStream getSystemStream() {
return getTerminal().getSystemStream();
}
}

View File

@ -18,8 +18,6 @@
*/ */
package org.apache.maven.jline; package org.apache.maven.jline;
import java.io.IOError;
import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import org.apache.maven.api.services.MessageBuilder; import org.apache.maven.api.services.MessageBuilder;
@ -43,14 +41,12 @@ public class MessageUtils {
static PrintStream prevErr; static PrintStream prevErr;
public static void systemInstall() { public static void systemInstall() {
try { terminal = new FastTerminal(
terminal = TerminalBuilder.builder().name("Maven").dumb(true).build(); () -> TerminalBuilder.builder().name("Maven").dumb(true).build(), t -> {
reader = LineReaderBuilder.builder().terminal(terminal).build(); reader = LineReaderBuilder.builder().terminal(t).build();
AnsiConsole.setTerminal(terminal); AnsiConsole.setTerminal(t);
AnsiConsole.systemInstall(); AnsiConsole.systemInstall();
} catch (IOException e) { });
throw new IOError(e);
}
} }
public static void registerShutdownHook() { public static void registerShutdownHook() {

View File

@ -34,11 +34,11 @@ import static org.apache.maven.jline.MessageUtils.builder;
*/ */
public class MavenSimpleLogger extends ExtSimpleLogger { public class MavenSimpleLogger extends ExtSimpleLogger {
private final String traceRenderedLevel = builder().trace("TRACE").build(); private String traceRenderedLevel;
private final String debugRenderedLevel = builder().debug("DEBUG").build(); private String debugRenderedLevel;
private final String infoRenderedLevel = builder().info("INFO").build(); private String infoRenderedLevel;
private final String warnRenderedLevel = builder().warning("WARNING").build(); private String warnRenderedLevel;
private final String errorRenderedLevel = builder().error("ERROR").build(); private String errorRenderedLevel;
static Consumer<String> logSink; static Consumer<String> logSink;
@ -52,6 +52,13 @@ public class MavenSimpleLogger extends ExtSimpleLogger {
@Override @Override
protected String renderLevel(int level) { protected String renderLevel(int level) {
if (traceRenderedLevel == null) {
traceRenderedLevel = builder().trace("TRACE").build();
debugRenderedLevel = builder().debug("DEBUG").build();
infoRenderedLevel = builder().info("INFO").build();
warnRenderedLevel = builder().warning("WARNING").build();
errorRenderedLevel = builder().error("ERROR").build();
}
switch (level) { switch (level) {
case LOG_LEVEL_TRACE: case LOG_LEVEL_TRACE:
return traceRenderedLevel; return traceRenderedLevel;