diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java index ca910ab3a..9e58fcbf6 100644 --- a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonParameters.java @@ -257,6 +257,10 @@ public int maxLostKeepAlive() { return property(Environment.DAEMON_MAX_LOST_KEEP_ALIVE).orFail().asInt(); } + public boolean noBuffering() { + return property(Environment.MVND_NO_BUFERING).orFail().asBoolean(); + } + public static String findDefaultMultimoduleProjectDirectory(Path pwd) { Path dir = pwd; do { diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java index 57ec61afa..ada0dcca6 100644 --- a/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java +++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java @@ -62,9 +62,10 @@ public static void main(String[] argv) throws Exception { } } - try (TerminalOutput output = new TerminalOutput(logFile)) { + DaemonParameters parameters = new DaemonParameters(); + try (TerminalOutput output = new TerminalOutput(parameters.noBuffering(), logFile)) { try { - new DefaultClient(new DaemonParameters()).execute(output, args); + new DefaultClient(parameters).execute(output, args); } catch (DaemonException.InterruptedException e) { final AttributedStyle s = new AttributedStyle().bold().foreground(AttributedStyle.RED); String str = new AttributedString(System.lineSeparator() + "Canceled by user", s).toAnsi(); diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java b/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java index 3decc805c..e63452748 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java +++ b/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java @@ -27,11 +27,20 @@ * Collects system properties and environment variables used by mvnd client or server. */ public enum Environment { + // + // Log properties + // LOGBACK_CONFIGURATION_FILE("logback.configurationFile", null, null, false), + // + // System properties + // JAVA_HOME("java.home", "JAVA_HOME", null, false), MVND_HOME("mvnd.home", "MVND_HOME", null, false), USER_HOME("user.home", null, null, false), USER_DIR("user.dir", null, null, false), + // + // Maven properties + // MAVEN_REPO_LOCAL("maven.repo.local", null, null, false), MAVEN_SETTINGS("maven.settings", null, null, false) { @Override @@ -45,8 +54,16 @@ public String asCommandLineProperty(String value) { } }, MAVEN_MULTIMODULE_PROJECT_DIRECTORY("maven.multiModuleProjectDirectory", null, null, false), + // + // mvnd properties + // MVND_PROPERTIES_PATH("mvnd.properties.path", "MVND_PROPERTIES_PATH", null, false), MVND_DAEMON_STORAGE("mvnd.daemon.storage", null, null, false), + /** + * Property that can be set to avoid buffering the output and display events continuously, closer to the usual maven + * display. + */ + MVND_NO_BUFERING("mvnd.noBuffering", null, "false", false), /** * The path to the daemon registry */ diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/logging/TerminalOutput.java b/common/src/main/java/org/jboss/fuse/mvnd/common/logging/TerminalOutput.java index bfeabf539..84e535324 100644 --- a/common/src/main/java/org/jboss/fuse/mvnd/common/logging/TerminalOutput.java +++ b/common/src/main/java/org/jboss/fuse/mvnd/common/logging/TerminalOutput.java @@ -27,7 +27,6 @@ import java.util.Deque; import java.util.LinkedHashMap; import java.util.List; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -53,8 +52,8 @@ */ public class TerminalOutput implements ClientOutput { + public static final int CTRL_B = 'B' & 0x1f; public static final int CTRL_L = 'L' & 0x1f; - public static final int CTRL_M = 'M' & 0x1f; private final Terminal terminal; @@ -65,7 +64,6 @@ public class TerminalOutput implements ClientOutput { private final Thread reader; private volatile Exception exception; private volatile boolean closing; - private final CountDownLatch closed = new CountDownLatch(1); private final long start; private final ReadWriteLock readInput = new ReentrantReadWriteLock(); @@ -90,6 +88,8 @@ public class TerminalOutput implements ClientOutput { private int doneProjects = 0; private String buildStatus; private boolean displayDone = false; + private boolean noBuffering; + private boolean dumb; /** * {@link Project} is owned by the display loop thread and is accessed only from there. Therefore it does not need @@ -105,9 +105,11 @@ public Project(String id) { } } - public TerminalOutput(Path logFile) throws IOException { + public TerminalOutput(boolean noBuffering, Path logFile) throws IOException { this.start = System.currentTimeMillis(); this.terminal = TerminalBuilder.terminal(); + this.dumb = terminal.getType().startsWith("dumb"); + this.noBuffering = noBuffering; terminal.enterRawMode(); Thread mainThread = Thread.currentThread(); daemonDispatch = m -> { @@ -119,9 +121,13 @@ public TerminalOutput(Path logFile) throws IOException { sig -> daemonDispatch.accept(Message.CANCEL_BUILD_SINGLETON)); this.display = new Display(terminal, false); this.log = logFile == null ? new MessageCollector() : new FileLog(logFile); - final Thread r = new Thread(this::readInputLoop); - r.start(); - this.reader = r; + if (!dumb) { + final Thread r = new Thread(this::readInputLoop); + r.start(); + this.reader = r; + } else { + this.reader = null; + } } @Override @@ -232,15 +238,19 @@ private boolean doAccept(Message entry) { } case Message.DISPLAY: { Message.StringMessage d = (Message.StringMessage) entry; - display.update(Collections.emptyList(), 0); + clearDisplay(); terminal.writer().printf("%s%n", d.getMessage()); break; } case Message.PROMPT: { Message.Prompt prompt = (Message.Prompt) entry; + if (dumb) { + terminal.writer().println(""); + break; + } readInput.writeLock().lock(); try { - display.update(Collections.emptyList(), 0); + clearDisplay(); terminal.writer().printf("[%s] %s", prompt.getProjectId(), prompt.getMessage()); terminal.flush(); StringBuilder sb = new StringBuilder(); @@ -273,29 +283,21 @@ private boolean doAccept(Message entry) { } case Message.BUILD_LOG_MESSAGE: { StringMessage sm = (StringMessage) entry; - if (closing) { - try { - closed.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - System.err.println(sm.getMessage()); - } else { - log.accept(sm.getMessage()); - } + log.accept(sm.getMessage()); break; } case Message.PROJECT_LOG_MESSAGE: { final ProjectEvent bm = (ProjectEvent) entry; - if (closing) { - try { - closed.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + final Project prj = projects.computeIfAbsent(bm.getProjectId(), Project::new); + if (noBuffering || dumb) { + String msg; + if (maxThreads > 1) { + msg = String.format("[%s] %s", bm.getProjectId(), bm.getMessage()); + } else { + msg = bm.getMessage(); } - System.err.println(bm.getMessage()); + log.accept(msg); } else { - final Project prj = projects.computeIfAbsent(bm.getProjectId(), Project::new); prj.log.add(bm.getMessage()); } break; @@ -309,8 +311,17 @@ private boolean doAccept(Message entry) { case '-': linesPerProject = Math.max(0, linesPerProject - 1); break; + case CTRL_B: + noBuffering = !noBuffering; + if (noBuffering) { + projects.values().stream().flatMap(p -> p.log.stream()).forEach(log); + projects.clear(); + } else { + clearDisplay(); + } + break; case CTRL_L: - display.update(Collections.emptyList(), 0); + clearDisplay(); break; case CTRL_M: displayDone = !displayDone; @@ -344,7 +355,7 @@ void readInputLoop() { if (c == -1) { break; } - if (c == '+' || c == '-' || c == CTRL_L || c == CTRL_M) { + if (c == '+' || c == '-' || c == CTRL_L || c == CTRL_M || c == CTRL_B) { accept(Message.keyboardInput((char) c)); } readInput.readLock().unlock(); @@ -360,7 +371,10 @@ void readInputLoop() { } private void clearDisplay() { - display.update(Collections.emptyList(), 0); + if (!noBuffering && !dumb) { + display.update(Collections.emptyList(), 0); + } + } private void displayDone() { @@ -376,18 +390,27 @@ private void displayDone() { @Override public void close() throws Exception { closing = true; - reader.interrupt(); + if (reader != null) { + reader.interrupt(); + reader.join(); + } log.close(); - reader.join(); terminal.handle(Terminal.Signal.INT, previousIntHandler); terminal.close(); - closed.countDown(); if (exception != null) { throw exception; } } private void update() { + if (noBuffering || dumb) { + try { + log.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return; + } // no need to refresh the display at every single step final Size size = terminal.getSize(); final int rows = size.getRows(); diff --git a/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/AbstractLoggingSpy.java b/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/AbstractLoggingSpy.java index 727148f3a..72302ce0b 100644 --- a/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/AbstractLoggingSpy.java +++ b/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/AbstractLoggingSpy.java @@ -24,12 +24,8 @@ public abstract class AbstractLoggingSpy { public static AbstractLoggingSpy instance() { if (instance == null) { - if ("mvns".equals(System.getProperty("mvnd.logging", "mvn"))) { - instance = new MavenLoggingSpy(); - } else { - instance = new AbstractLoggingSpy() { - }; - } + instance = new AbstractLoggingSpy() { + }; } return instance; } diff --git a/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/MavenLoggingSpy.java b/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/MavenLoggingSpy.java deleted file mode 100644 index 40b9d3c78..000000000 --- a/daemon/src/main/java/org/jboss/fuse/mvnd/logging/smart/MavenLoggingSpy.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2019 the original author or authors. - * - * Licensed 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.jboss.fuse.mvnd.logging.smart; - -import java.io.IOError; -import org.apache.maven.execution.MavenSession; -import org.jboss.fuse.mvnd.common.Message; -import org.jboss.fuse.mvnd.common.Message.BuildStarted; -import org.jboss.fuse.mvnd.common.logging.TerminalOutput; - -public class MavenLoggingSpy extends AbstractLoggingSpy { - - private TerminalOutput output; - - public MavenLoggingSpy() { - } - - @Override - protected void onStartSession(MavenSession session) { - try { - output = new TerminalOutput(null); - output.accept(new BuildStarted( - session.getTopLevelProject().getName(), - session.getAllProjects().size(), - session.getRequest().getDegreeOfConcurrency())); - } catch (Exception e) { - throw new IOError(e); - } - } - - @Override - protected void onFinishSession() { - try { - output.close(); - } catch (Exception e) { - throw new IOError(e); - } - } - - @Override - protected void onStartProject(String projectId, String display) { - super.onStartProject(projectId, display); - output.accept(Message.projectStarted(projectId, display)); - } - - @Override - protected void onStopProject(String projectId, String display) { - output.accept(Message.projectStopped(projectId, display)); - } - - @Override - protected void onStartMojo(String projectId, String display) { - super.onStartMojo(projectId, display); - output.accept(Message.mojoStarted(projectId, display)); - } - - @Override - protected void onProjectLog(String projectId, String message) { - super.onProjectLog(projectId, message); - output.accept(projectId == null ? Message.log(message) : Message.log(projectId, message)); - } - -} diff --git a/dist/src/main/distro/bin/mvns.sh b/dist/src/main/distro/bin/mvns.sh deleted file mode 100755 index 4175b6662..000000000 --- a/dist/src/main/distro/bin/mvns.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/bin/sh - -# 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. - -# ----------------------------------------------------------------------------- -# Apache Maven Startup Script -# -# Environment Variable Prerequisites -# -# JAVA_HOME Must point at your Java Development Kit installation. -# MAVEN_OPTS (Optional) Java runtime options used when Maven is executed. -# MAVEN_SKIP_RC (Optional) Flag to disable loading of mavenrc files. -# ----------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -mingw=false; -case "`uname`" in - CYGWIN*) cygwin=true;; - MINGW*) mingw=true;; -esac - -## resolve links - $0 may be a link to Maven's home -PRG="$0" - -# need this for relative symlinks -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi -done - -saveddir=`pwd` - -MVND_HOME=`dirname "$PRG"`/.. - -# make it fully qualified -MVND_HOME=`cd "$MVND_HOME" && pwd` - -cd "$saveddir" - -# For Cygwin, ensure paths are in Unix format before anything is touched -if $cygwin ; then - [ -n "$MVND_HOME" ] && - MVND_HOME=`cygpath --unix "$MVND_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For MinGW, ensure paths are in Unix format before anything is touched -if $mingw ; then - [ -n "$MVND_HOME" ] && - MVND_HOME=`(cd "$MVND_HOME"; pwd)` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`(cd "$JAVA_HOME"; pwd)` - # TODO classpath? -fi - -if [ -z "$JAVA_HOME" ] ; then - JAVACMD=`which java` -else - JAVACMD="$JAVA_HOME/bin/java" -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "The JAVA_HOME environment variable is not defined correctly" >&2 - echo "This environment variable is needed to run this program" >&2 - echo "NB: JAVA_HOME should point to a JDK not a JRE" >&2 - exit 1 -fi - -CLASSWORLDS_JAR=`echo "${MVND_HOME}"/mvn/boot/plexus-classworlds-*.jar` -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - [ -n "$MVND_HOME" ] && - MVND_HOME=`cygpath --path --windows "$MVND_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$CLASSWORLDS_JAR" ] && - CLASSWORLDS_JAR=`cygpath --path --windows "$CLASSWORLDS_JAR"` -fi - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { -( - basedir=`find_file_argument_basedir "$@"` - wdir="${basedir}" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - wdir=`cd "$wdir/.."; pwd` - done - echo "${basedir}" -) -} - -find_file_argument_basedir() { -( - basedir=`pwd` - - found_file_switch=0 - for arg in "$@"; do - if [ ${found_file_switch} -eq 1 ]; then - if [ -d "${arg}" ]; then - basedir=`cd "${arg}" && pwd -P` - elif [ -f "${arg}" ]; then - basedir=`dirname "${arg}"` - basedir=`cd "${basedir}" && pwd -P` - if [ ! -d "${basedir}" ]; then - echo "Directory ${basedir} extracted from the -f/--file command-line argument ${arg} does not exist" >&2 - exit 1 - fi - else - echo "POM file ${arg} specified with the -f/--file command line argument does not exist" >&2 - exit 1 - fi - break - fi - if [ "$arg" = "-f" -o "$arg" = "--file" ]; then - found_file_switch=1 - fi - done - echo "${basedir}" -) -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "`tr -s '\r\n' ' ' < "$1"`" - fi -} - -MAVEN_PROJECTBASEDIR="${MAVEN_BASEDIR:-`find_maven_basedir "$@"`}" -MAVEN_OPTS="`concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config"` $MAVEN_OPTS" - -# For Cygwin, switch project base directory path to Windows format before -# executing Maven otherwise this will cause Maven not to consider it. -if $cygwin ; then - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -export MAVEN_PROJECTBASEDIR - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "${CLASSWORLDS_JAR}" \ - "-Dlogback.configurationFile=${MVND_HOME}/mvn/conf/logging/logback.xml" \ - -Dmvnd.logging=mvns \ - "-Dclassworlds.conf=${MVND_HOME}/mvn/bin/m2.conf" \ - "-Dmvnd.home=${MVND_HOME}" \ - "-Dmaven.home=${MVND_HOME}/mvn" \ - "-Dlibrary.jansi.path=${MVND_HOME}/mvn/lib/jansi-native" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${CLASSWORLDS_LAUNCHER} --builder smart --threads 0.5C "$@" -