Skip to content

Commit

Permalink
Support for interactive prompts, fixes #180
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet committed Nov 6, 2020
1 parent 2deea59 commit 2a0ed8f
Show file tree
Hide file tree
Showing 13 changed files with 628 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ protected void doReceive() {
try {
while (running.get()) {
Message m = connection.receive();
if (m == null) {
break;
}
queue.put(m);
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,13 @@ public ExecutionResult execute(ClientOutput output, List<String> argv) {
output.accept(bm.getProjectId(), bm.getMessage());
} else if (m == Message.KEEP_ALIVE_SINGLETON) {
output.keepAlive();
} else if (m instanceof Message.Display) {
Message.Display d = (Message.Display) m;
output.display(d.getProjectId(), d.getMessage());
} else if (m instanceof Message.Prompt) {
Message.Prompt p = (Message.Prompt) m;
String response = output.prompt(p.getProjectId(), p.getMessage(), p.isPassword());
daemon.dispatch(new Message.PromptResponse(p.getProjectId(), p.getUid(), response));
}
}
}
Expand Down
156 changes: 156 additions & 0 deletions common/src/main/java/org/jboss/fuse/mvnd/common/Message.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public abstract class Message {
static final int KEEP_ALIVE = 4;
static final int STOP = 5;
static final int BUILD_STARTED = 6;
static final int DISPLAY = 7;
static final int PROMPT = 8;
static final int PROMPT_RESPONSE = 9;

public static final SimpleMessage KEEP_ALIVE_SINGLETON = new SimpleMessage(Message.KEEP_ALIVE, "KEEP_ALIVE");
public static final SimpleMessage STOP_SINGLETON = new SimpleMessage(Message.STOP, "STOP");
Expand All @@ -59,6 +62,12 @@ public static Message read(DataInputStream input) throws IOException {
return SimpleMessage.KEEP_ALIVE_SINGLETON;
case STOP:
return SimpleMessage.STOP_SINGLETON;
case DISPLAY:
return Display.read(input);
case PROMPT:
return Prompt.read(input);
case PROMPT_RESPONSE:
return PromptResponse.read(input);
}
throw new IllegalStateException("Unexpected message type: " + type);
}
Expand Down Expand Up @@ -489,4 +498,151 @@ public void write(DataOutputStream output) throws IOException {
}
}

public static class Display extends Message {

final String projectId;
final String message;

public static Message read(DataInputStream input) throws IOException {
String projectId = readUTF(input);
String message = readUTF(input);
return new Display(projectId, message);
}

public Display(String projectId, String message) {
this.projectId = projectId;
this.message = message;
}

public String getProjectId() {
return projectId;
}

public String getMessage() {
return message;
}

@Override
public String toString() {
return "Display{" +
"projectId='" + projectId + '\'' +
", message='" + message + '\'' +
'}';
}

@Override
public void write(DataOutputStream output) throws IOException {
output.write(DISPLAY);
writeUTF(output, projectId);
writeUTF(output, message);
}
}

public static class Prompt extends Message {

final String projectId;
final String uid;
final String message;
final boolean password;

public static Message read(DataInputStream input) throws IOException {
String projectId = Message.readUTF(input);
String uid = Message.readUTF(input);
String message = Message.readUTF(input);
boolean password = input.readBoolean();
return new Prompt(projectId, uid, message, password);
}

public Prompt(String projectId, String uid, String message, boolean password) {
this.projectId = projectId;
this.uid = uid;
this.message = message;
this.password = password;
}

public String getProjectId() {
return projectId;
}

public String getUid() {
return uid;
}

public String getMessage() {
return message;
}

public boolean isPassword() {
return password;
}

@Override
public String toString() {
return "Prompt{" +
"projectId='" + projectId + '\'' +
", uid='" + uid + '\'' +
", message='" + message + '\'' +
", password=" + password +
'}';
}

@Override
public void write(DataOutputStream output) throws IOException {
output.write(PROMPT);
writeUTF(output, projectId);
writeUTF(output, uid);
writeUTF(output, message);
output.writeBoolean(password);
}
}

public static class PromptResponse extends Message {

final String projectId;
final String uid;
final String message;

public static Message read(DataInputStream input) throws IOException {
String projectId = Message.readUTF(input);
String uid = Message.readUTF(input);
String message = Message.readUTF(input);
return new PromptResponse(projectId, uid, message);
}

public PromptResponse(String projectId, String uid, String message) {
this.projectId = projectId;
this.uid = uid;
this.message = message;
}

public String getProjectId() {
return projectId;
}

public String getUid() {
return uid;
}

public String getMessage() {
return message;
}

@Override
public String toString() {
return "PromptResponse{" +
"projectId='" + projectId + '\'' +
", uid='" + uid + '\'' +
", message='" + message + '\'' +
'}';
}

@Override
public void write(DataOutputStream output) throws IOException {
output.write(PROMPT_RESPONSE);
writeUTF(output, projectId);
writeUTF(output, uid);
writeUTF(output, message);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,7 @@ public interface ClientOutput extends AutoCloseable {

void buildStatus(String status);

void display(String projectId, String message);

String prompt(String projectId, String message, boolean password);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.stream.Collector;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -61,6 +65,7 @@ public class TerminalOutput implements ClientOutput {
private volatile boolean closing;
private final CountDownLatch closed = new CountDownLatch(1);
private final long start;
private final ReadWriteLock readInput = new ReentrantReadWriteLock();

private volatile String name;
private volatile int totalProjects;
Expand All @@ -79,19 +84,28 @@ enum EventType {
ERROR,
END_OF_STREAM,
INPUT,
KEEP_ALIVE
KEEP_ALIVE,
DISPLAY,
PROMPT,
PROMPT_PASSWORD
}

static class Event {
public static final Event KEEP_ALIVE = new Event(EventType.KEEP_ALIVE, null, null);
public final EventType type;
public final String projectId;
public final String message;
public final SynchronousQueue<String> response;

public Event(EventType type, String projectId, String message) {
this(type, projectId, message, null);
}

public Event(EventType type, String projectId, String message, SynchronousQueue<String> response) {
this.type = type;
this.projectId = projectId;
this.message = message;
this.response = response;
}
}

Expand Down Expand Up @@ -202,17 +216,44 @@ public void buildStatus(String status) {
}
}

@Override
public void display(String projectId, String message) {
try {
queue.put(new Event(EventType.DISPLAY, projectId, message));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

@Override
public String prompt(String projectId, String message, boolean password) {
String response = null;
try {
SynchronousQueue<String> sq = new SynchronousQueue<>();
queue.put(new Event(password ? EventType.PROMPT_PASSWORD : EventType.PROMPT, projectId, message, sq));
response = sq.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return response;
}

void readInputLoop() {
try {
while (!closing) {
int c = terminal.reader().read(10);
if (c == -1) {
break;
}
if (c == '+' || c == '-' || c == CTRL_L || c == CTRL_M) {
queue.add(new Event(EventType.INPUT, null, Character.toString((char) c)));
if (readInput.readLock().tryLock(10, TimeUnit.MILLISECONDS)) {
int c = terminal.reader().read(10);
if (c == -1) {
break;
}
if (c == '+' || c == '-' || c == CTRL_L || c == CTRL_M) {
queue.add(new Event(EventType.INPUT, null, Character.toString((char) c)));
}
readInput.readLock().unlock();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (InterruptedIOException e) {
Thread.currentThread().interrupt();
} catch (IOException e) {
Expand Down Expand Up @@ -289,6 +330,42 @@ void displayLoop() {
break;
}
break;
case DISPLAY:
display.update(Collections.emptyList(), 0);
terminal.writer().printf("[%s] %s%n", entry.projectId, entry.message);
break;
case PROMPT:
case PROMPT_PASSWORD: {
readInput.writeLock().lock();
try {
display.update(Collections.emptyList(), 0);
terminal.writer().printf("[%s] %s", entry.projectId, entry.message);
terminal.flush();
StringBuilder sb = new StringBuilder();
while (true) {
int c = terminal.reader().read();
if (c < 0) {
break;
} else if (c == '\n' || c == '\r') {
entry.response.put(sb.toString());
terminal.writer().println();
break;
} else if (c == 127) {
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
terminal.writer().write("\b \b");
terminal.writer().flush();
}
} else {
terminal.writer().print((char) c);
terminal.writer().flush();
sb.append((char) c);
}
}
} finally {
readInput.writeLock().unlock();
}
}
}
}
entries.clear();
Expand Down
5 changes: 5 additions & 0 deletions daemon/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
<groupId>io.takari.aether</groupId>
<artifactId>takari-local-repository</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-interactivity-api</artifactId>
<version>1.0</version>
</dependency>

<!-- Logging -->
<dependency>
Expand Down
3 changes: 3 additions & 0 deletions daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,9 @@ void container()
exportedArtifacts.addAll(extension.getExportedArtifacts());
exportedPackages.addAll(extension.getExportedPackages());
}
exportedPackages.add("org.codehaus.plexus.components.interactivity");
exportedPackages.add("org.jboss.fuse.mvnd.interactivity");
exportedArtifacts.add("org.codehaus.plexus:plexus-interactivity-api");

final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);

Expand Down
Loading

0 comments on commit 2a0ed8f

Please sign in to comment.