mvnd should fail cleanly with unknown CLI options, fixes #11

This commit is contained in:
Guillaume Nodet
2020-02-12 13:41:19 +01:00
parent 309939d56e
commit 7f4258d04e
3 changed files with 169 additions and 47 deletions

View File

@@ -30,8 +30,10 @@ import java.util.List;
import java.util.Properties;
import java.util.UUID;
import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.maven.cli.CLIReportingUtils;
import org.jboss.fuse.mvnd.daemon.Message.BuildEvent;
import org.jboss.fuse.mvnd.daemon.Message.BuildException;
import org.jboss.fuse.mvnd.daemon.Message.BuildMessage;
import org.jboss.fuse.mvnd.daemon.Message.MessageSerializer;
import org.jboss.fuse.mvnd.jpm.Process;
@@ -41,6 +43,8 @@ import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.jline.utils.Display;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,7 +61,7 @@ public class Client {
List<String> args = new ArrayList<>(Arrays.asList(argv));
// Print version if needed
boolean version = args.remove("-v") || args.remove("--version");
boolean version = args.remove("-v") || args.remove("-version") || args.remove("--version");
boolean showVersion = args.contains("-V") || args.contains("--show-version");
boolean debug = args.contains("-X") || args.contains("--debug");
if (version || showVersion || debug) {
@@ -134,9 +138,13 @@ public class Client {
Terminal terminal = TerminalBuilder.terminal();
Display display = new Display(terminal, false);
boolean exit = false;
BuildException error = null;
while (!exit) {
Message m = daemon.receive();
if (m instanceof BuildEvent) {
if (m instanceof BuildException) {
error = (BuildException) m;
exit = true;
} else if (m instanceof BuildEvent) {
BuildEvent be = (BuildEvent) m;
switch (be.getType()) {
case BuildStarted:
@@ -167,6 +175,16 @@ public class Client {
}
}
display.update(Collections.emptyList(), 0);
if (error != null) {
AttributedStyle s = new AttributedStyle().bold().foreground(AttributedStyle.RED);
String msg;
if (UnrecognizedOptionException.class.getName().equals(error.getClassName())) {
msg = "Unable to parse command line options: " + error.getMessage();
} else {
msg = error.getClassName() + ": " + error.getMessage();
}
terminal.writer().println(new AttributedString(msg, s).toAnsi());
}
terminal.flush();
LOGGER.debug("Done receiving, printing log");

View File

@@ -23,6 +23,8 @@ import java.io.UTFDataFormatException;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.plexus.util.ExceptionUtils;
public abstract class Message {
final long timestamp = System.nanoTime();
@@ -53,6 +55,52 @@ public abstract class Message {
public String getProjectDir() {
return projectDir;
}
@Override
public String toString() {
return "BuildRequest{" +
"args=" + args +
", workingDir='" + workingDir + '\'' +
", projectDir='" + projectDir + '\'' +
'}';
}
}
public static class BuildException extends Message {
final String message;
final String className;
final String stackTrace;
public BuildException(Throwable t) {
this(t.getMessage(), t.getClass().getName(), ExceptionUtils.getStackTrace(t));
}
public BuildException(String message, String className, String stackTrace) {
this.message = message;
this.className = className;
this.stackTrace = stackTrace;
}
public String getMessage() {
return message;
}
public String getClassName() {
return className;
}
public String getStackTrace() {
return stackTrace;
}
@Override
public String toString() {
return "BuildException{" +
"message='" + message + '\'' +
", className='" + className + '\'' +
", stackTrace='" + stackTrace + '\'' +
'}';
}
}
public static class BuildEvent extends Message {
@@ -114,6 +162,7 @@ public abstract class Message {
final int BUILD_REQUEST = 0;
final int BUILD_EVENT = 1;
final int BUILD_MESSAGE = 2;
final int BUILD_EXCEPTION = 3;
@Override
public Message read(DataInputStream input) throws EOFException, Exception {
@@ -128,6 +177,8 @@ public abstract class Message {
return readBuildEvent(input);
case BUILD_MESSAGE:
return readBuildMessage(input);
case BUILD_EXCEPTION:
return readBuildException(input);
}
throw new IllegalStateException("Unexpected message type: " + type);
}
@@ -143,6 +194,9 @@ public abstract class Message {
} else if (value instanceof BuildMessage) {
output.write(BUILD_MESSAGE);
writeBuildMessage(output, (BuildMessage) value);
} else if (value instanceof BuildException) {
output.write(BUILD_EXCEPTION);
writeBuildException(output, (BuildException) value);
} else {
throw new IllegalStateException();
}
@@ -183,6 +237,19 @@ public abstract class Message {
writeUTF(output, value.message);
}
private BuildException readBuildException(DataInputStream input) throws IOException {
String message = readUTF(input);
String className = readUTF(input);
String stackTrace = readUTF(input);
return new BuildException(message, className, stackTrace);
}
private void writeBuildException(DataOutputStream output, BuildException value) throws IOException {
writeUTF(output, value.message);
writeUTF(output, value.className);
writeUTF(output, value.stackTrace);
}
private List<String> readStringList(DataInputStream input) throws IOException {
ArrayList<String> l = new ArrayList<>();
int nb = input.readInt();

View File

@@ -42,6 +42,7 @@ import org.jboss.fuse.mvnd.daemon.DaemonExpiration.DaemonExpirationStatus;
import org.jboss.fuse.mvnd.daemon.DaemonExpiration.DaemonExpirationStrategy;
import org.jboss.fuse.mvnd.daemon.Message.BuildEvent;
import org.jboss.fuse.mvnd.daemon.Message.BuildEvent.Type;
import org.jboss.fuse.mvnd.daemon.Message.BuildException;
import org.jboss.fuse.mvnd.daemon.Message.BuildMessage;
import org.jboss.fuse.mvnd.daemon.Message.BuildRequest;
import org.jboss.fuse.mvnd.daemon.Message.MessageSerializer;
@@ -386,48 +387,8 @@ public class Server implements AutoCloseable, Runnable {
PriorityBlockingQueue<Message> queue = new PriorityBlockingQueue<Message>(64,
Comparator.comparingInt(this::getClassOrder).thenComparingLong(Message::timestamp));
AbstractLoggingSpy.instance(new AbstractLoggingSpy() {
@Override
public void init(Context context) throws Exception {
super.init(context);
queue.add(new BuildEvent(Type.BuildStarted, "", ""));
}
@Override
public void close() throws Exception {
sendBuildMessages();
queue.add(new BuildEvent(Type.BuildStopped, "", ""));
queue.add(STOP);
super.close();
}
@Override
protected void onStartProject(ProjectBuild project) {
sendEvent(Type.ProjectStarted, project);
}
@Override
protected void onStopProject(ProjectBuild project) {
sendEvent(Type.ProjectStopped, project);
}
@Override
protected void onStartMojo(ProjectBuild project) {
sendEvent(Type.MojoStarted, project);
}
@Override
protected void onStopMojo(ProjectBuild project) {
sendEvent(Type.MojoStopped, project);
}
private void sendEvent(Type type, ProjectBuild project) {
String projectId = project.projectId();
String disp = project.toDisplay().toAnsi(256, false);
queue.add(new Message.BuildEvent(type, projectId, disp));
sendBuildMessages();
}
private synchronized void sendBuildMessages() {
events.stream()
.map(s -> s.endsWith("\n") ? s.substring(0, s.length() - 1) : s)
.map(Message.BuildMessage::new).forEachOrdered(queue::add);
events.clear();
}
});
DaemonLoggingSpy loggingSpy = new DaemonLoggingSpy(queue);
AbstractLoggingSpy.instance(loggingSpy);
Thread pumper = new Thread(() -> {
try {
while (true) {
@@ -451,9 +412,16 @@ public class Server implements AutoCloseable, Runnable {
}
});
pumper.start();
cli.doMain(req);
LOGGER.info("Build finished, finishing message dispatch");
pumper.join();
try {
cli.doMain(req);
LOGGER.info("Build finished, finishing message dispatch");
loggingSpy.finish();
} catch (Throwable t) {
LOGGER.error("Error while building project", t);
loggingSpy.fail(t);
} finally {
pumper.join();
}
} catch (Throwable t) {
LOGGER.error("Error while building project", t);
} finally {
@@ -470,6 +438,8 @@ public class Server implements AutoCloseable, Runnable {
return be.getType() == Type.BuildStopped ? 98 : 1;
} else if (m instanceof BuildMessage) {
return 2;
} else if (m instanceof BuildException) {
return 97;
} else if (m == STOP) {
return 99;
} else {
@@ -517,4 +487,71 @@ public class Server implements AutoCloseable, Runnable {
public long getLastBusy() {
return info.getLastBusy();
}
private static class DaemonLoggingSpy extends AbstractLoggingSpy {
private final PriorityBlockingQueue<Message> queue;
public DaemonLoggingSpy(PriorityBlockingQueue<Message> queue) {
this.queue = queue;
}
@Override
public void init(Context context) throws Exception {
super.init(context);
queue.add(new BuildEvent(Type.BuildStarted, "", ""));
}
@Override
public void close() throws Exception {
LOGGER.error("Closing spy", new Throwable());
sendBuildMessages();
super.close();
}
public void finish() throws Exception {
queue.add(new BuildEvent(Type.BuildStopped, "", ""));
queue.add(STOP);
}
public void fail(Throwable t) throws Exception {
queue.add(new BuildException(t));
queue.add(STOP);
}
@Override
protected void onStartProject(ProjectBuild project) {
sendEvent(Type.ProjectStarted, project);
}
@Override
protected void onStopProject(ProjectBuild project) {
sendEvent(Type.ProjectStopped, project);
}
@Override
protected void onStartMojo(ProjectBuild project) {
sendEvent(Type.MojoStarted, project);
}
@Override
protected void onStopMojo(ProjectBuild project) {
sendEvent(Type.MojoStopped, project);
}
private void sendEvent(Type type, ProjectBuild project) {
String projectId = project.projectId();
String disp = project.toDisplay().toAnsi(256, false);
queue.add(new BuildEvent(type, projectId, disp));
sendBuildMessages();
}
private synchronized void sendBuildMessages() {
if (events != null) {
events.stream()
.map(s -> s.endsWith("\n") ? s.substring(0, s.length() - 1) : s)
.map(BuildMessage::new).forEachOrdered(queue::add);
events.clear();
}
}
}
}