diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java index 7567e54ac5..a8085253a8 100644 --- a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java +++ b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java @@ -58,9 +58,9 @@ public class ExecutionEventLogger extends AbstractExecutionListener { private final MessageBuilderFactory messageBuilderFactory; private final Logger logger; - private final int terminalWidth; - private final int lineLength; - private final int maxProjectNameLength; + private int terminalWidth; + private int lineLength; + private int maxProjectNameLength; private int totalProjects; private volatile int currentVisitedProjectCount; @@ -69,17 +69,13 @@ public class ExecutionEventLogger extends AbstractExecutionListener { } public ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger logger) { - this(messageBuilderFactory, logger, messageBuilderFactory.getTerminalWidth()); + this(messageBuilderFactory, logger, -1); } public ExecutionEventLogger(MessageBuilderFactory messageBuilderFactory, Logger logger, int terminalWidth) { this.logger = Objects.requireNonNull(logger, "logger cannot be null"); this.messageBuilderFactory = messageBuilderFactory; - this.terminalWidth = Math.min( - 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; + this.terminalWidth = terminalWidth; } private static String chars(char c, int count) { @@ -100,9 +96,23 @@ public class ExecutionEventLogger extends AbstractExecutionListener { 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 public void projectDiscoveryStarted(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); logger.info("Scanning for projects..."); } } @@ -110,6 +120,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void sessionStarted(ExecutionEvent event) { if (logger.isInfoEnabled() && event.getSession().getProjects().size() > 1) { + init(); infoLine('-'); infoMain("Reactor Build Order:"); @@ -136,6 +147,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void sessionEnded(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); if (event.getSession().getProjects().size() > 1) { logReactorSummary(event.getSession()); } @@ -280,6 +292,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void projectSkipped(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); logger.info(""); infoLine('-'); @@ -293,6 +306,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void projectStarted(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); MavenProject project = event.getProject(); logger.info(""); @@ -354,6 +368,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void mojoSkipped(ExecutionEvent event) { if (logger.isWarnEnabled()) { + init(); logger.warn( "Goal '{}' requires online mode for execution but Maven is currently offline, skipping", event.getMojoExecution().getGoal()); @@ -366,6 +381,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void mojoStarted(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); logger.info(""); MessageBuilder buffer = builder().strong("--- "); @@ -386,6 +402,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void forkStarted(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); logger.info(""); MessageBuilder buffer = builder().strong(">>> "); @@ -408,6 +425,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { @Override public void forkSucceeded(ExecutionEvent event) { if (logger.isInfoEnabled()) { + init(); logger.info(""); MessageBuilder buffer = builder().strong("<<< "); @@ -460,6 +478,7 @@ public class ExecutionEventLogger extends AbstractExecutionListener { public void forkedProjectStarted(ExecutionEvent event) { if (logger.isInfoEnabled() && event.getMojoExecution().getForkedExecutions().size() > 1) { + init(); logger.info(""); infoLine('>'); diff --git a/maven-jline/src/main/java/org/apache/maven/jline/FastTerminal.java b/maven-jline/src/main/java/org/apache/maven/jline/FastTerminal.java new file mode 100644 index 0000000000..dbdbb06d4c --- /dev/null +++ b/maven-jline/src/main/java/org/apache/maven/jline/FastTerminal.java @@ -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; + + public FastTerminal(Callable builder, Consumer 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(); + } +} diff --git a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java index a0e8cc338c..878d773a75 100644 --- a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java +++ b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java @@ -18,8 +18,6 @@ */ package org.apache.maven.jline; -import java.io.IOError; -import java.io.IOException; import java.io.PrintStream; import org.apache.maven.api.services.MessageBuilder; @@ -43,14 +41,12 @@ public class MessageUtils { static PrintStream prevErr; public static void systemInstall() { - try { - terminal = TerminalBuilder.builder().name("Maven").dumb(true).build(); - reader = LineReaderBuilder.builder().terminal(terminal).build(); - AnsiConsole.setTerminal(terminal); - AnsiConsole.systemInstall(); - } catch (IOException e) { - throw new IOError(e); - } + terminal = new FastTerminal( + () -> TerminalBuilder.builder().name("Maven").dumb(true).build(), t -> { + reader = LineReaderBuilder.builder().terminal(t).build(); + AnsiConsole.setTerminal(t); + AnsiConsole.systemInstall(); + }); } public static void registerShutdownHook() { diff --git a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java b/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java index 46a794b160..749d60de88 100644 --- a/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java +++ b/maven-slf4j-provider/src/main/java/org/apache/maven/slf4j/MavenSimpleLogger.java @@ -34,11 +34,11 @@ import static org.apache.maven.jline.MessageUtils.builder; */ public class MavenSimpleLogger extends ExtSimpleLogger { - private final String traceRenderedLevel = builder().trace("TRACE").build(); - private final String debugRenderedLevel = builder().debug("DEBUG").build(); - private final String infoRenderedLevel = builder().info("INFO").build(); - private final String warnRenderedLevel = builder().warning("WARNING").build(); - private final String errorRenderedLevel = builder().error("ERROR").build(); + private String traceRenderedLevel; + private String debugRenderedLevel; + private String infoRenderedLevel; + private String warnRenderedLevel; + private String errorRenderedLevel; static Consumer logSink; @@ -52,6 +52,13 @@ public class MavenSimpleLogger extends ExtSimpleLogger { @Override 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) { case LOG_LEVEL_TRACE: return traceRenderedLevel;