Send transfer events to the client for better display, fixes #284 (#313)

This commit is contained in:
Guillaume Nodet
2021-01-08 20:28:02 +01:00
committed by GitHub
parent 0f438bdb09
commit 28ffaeaecc
7 changed files with 379 additions and 9 deletions

View File

@@ -48,6 +48,12 @@ public abstract class Message {
public static final int BUILD_STATUS = 14;
public static final int KEYBOARD_INPUT = 15;
public static final int CANCEL_BUILD = 16;
public static final int TRANSFER_INITIATED = 17;
public static final int TRANSFER_STARTED = 18;
public static final int TRANSFER_PROGRESSED = 19;
public static final int TRANSFER_CORRUPTED = 20;
public static final int TRANSFER_SUCCEEDED = 21;
public static final int TRANSFER_FAILED = 22;
public static final BareMessage KEEP_ALIVE_SINGLETON = new BareMessage(KEEP_ALIVE);
public static final BareMessage STOP_SINGLETON = new BareMessage(STOP);
@@ -93,6 +99,13 @@ public abstract class Message {
return StringMessage.read(type, input);
case CANCEL_BUILD:
return BareMessage.CANCEL_BUILD_SINGLETON;
case TRANSFER_INITIATED:
case TRANSFER_STARTED:
case TRANSFER_PROGRESSED:
case TRANSFER_CORRUPTED:
case TRANSFER_SUCCEEDED:
case TRANSFER_FAILED:
return TransferEvent.read(type, input);
}
throw new IllegalStateException("Unexpected message type: " + type);
}
@@ -760,6 +773,135 @@ public abstract class Message {
}
}
public static class TransferEvent extends Message {
public static final int INITIATED = 0;
public static final int STARTED = 1;
public static final int PROGRESSED = 2;
public static final int CORRUPTED = 3;
public static final int SUCCEEDED = 4;
public static final int FAILED = 5;
public static final int GET = 0;
public static final int GET_EXISTENCE = 1;
public static final int PUT = 2;
final String projectId;
final int request;
final String repositoryId;
final String repositoryUrl;
final String resourceName;
final long contentLength;
final long transferredBytes;
final String exception;
private TransferEvent(int type, String projectId, int request,
String repositoryId, String repositoryUrl,
String resourceName, long contentLength, long transferredBytes,
String exception) {
super(type);
this.projectId = projectId;
this.request = request;
this.repositoryId = repositoryId;
this.repositoryUrl = repositoryUrl;
this.resourceName = resourceName;
this.contentLength = contentLength;
this.transferredBytes = transferredBytes;
this.exception = exception;
}
public String getProjectId() {
return projectId;
}
public int getRequest() {
return request;
}
public String getRepositoryId() {
return repositoryId;
}
public String getRepositoryUrl() {
return repositoryUrl;
}
public String getResourceName() {
return resourceName;
}
public long getContentLength() {
return contentLength;
}
public long getTransferredBytes() {
return transferredBytes;
}
public String getException() {
return exception;
}
@Override
public String toString() {
return mnemonic() + "{" +
"projectId=" + projectId +
", request=" + request +
", repositoryId='" + repositoryId + '\'' +
", repositoryUrl='" + repositoryUrl + '\'' +
", resourceName='" + resourceName + '\'' +
", contentLength=" + contentLength +
", transferredBytes=" + transferredBytes +
", exception='" + exception + '\'' +
'}';
}
private String mnemonic() {
switch (type) {
case TRANSFER_INITIATED:
return "TransferInitiated";
case TRANSFER_STARTED:
return "TransferStarted";
case TRANSFER_PROGRESSED:
return "TransferProgressed";
case TRANSFER_CORRUPTED:
return "TransferCorrupted";
case TRANSFER_SUCCEEDED:
return "TransferSucceeded";
case TRANSFER_FAILED:
return "TransferFailed";
default:
throw new IllegalStateException("Unexpected type " + type);
}
}
@Override
public void write(DataOutputStream output) throws IOException {
super.write(output);
writeUTF(output, projectId);
output.writeByte(request);
writeUTF(output, repositoryId);
writeUTF(output, repositoryUrl);
writeUTF(output, resourceName);
output.writeLong(contentLength);
output.writeLong(transferredBytes);
writeUTF(output, exception);
}
public static TransferEvent read(int type, DataInputStream input) throws IOException {
String projectId = readUTF(input);
int request = input.readByte();
String repositoryId = readUTF(input);
String repositoryUrl = readUTF(input);
String resourceName = readUTF(input);
long contentLength = input.readLong();
long transferredBytes = input.readLong();
String exception = readUTF(input);
return new TransferEvent(type, projectId, request, repositoryId, repositoryUrl, resourceName,
contentLength, transferredBytes, exception);
}
}
public int getType() {
return type;
}
@@ -785,7 +927,7 @@ public abstract class Message {
}
public static StringMessage projectStarted(String projectId) {
return new StringMessage(Message.PROJECT_STARTED, projectId);
return new StringMessage(PROJECT_STARTED, projectId);
}
public static StringMessage projectStopped(String projectId) {
@@ -795,11 +937,18 @@ public abstract class Message {
public static Message mojoStarted(String artifactId, String pluginGroupId, String pluginArtifactId,
String pluginVersion, String mojo, String executionId) {
return new MojoStartedEvent(artifactId, pluginGroupId, pluginArtifactId, pluginVersion, mojo, executionId);
}
public static ProjectEvent display(String projectId, String message) {
return new ProjectEvent(Message.DISPLAY, projectId, message);
}
public static TransferEvent transfer(String projectId, int event, int request,
String repositoryId, String repositoryUrl,
String resourceName, long contentLength, long transferredBytes,
String exception) {
return new TransferEvent(event, projectId, request,
repositoryId, repositoryUrl, resourceName, contentLength, transferredBytes, exception);
}
}

View File

@@ -23,10 +23,12 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -47,6 +49,8 @@ import org.mvndaemon.mvnd.common.Message.BuildStarted;
import org.mvndaemon.mvnd.common.Message.MojoStartedEvent;
import org.mvndaemon.mvnd.common.Message.ProjectEvent;
import org.mvndaemon.mvnd.common.Message.StringMessage;
import org.mvndaemon.mvnd.common.Message.TransferEvent;
import org.mvndaemon.mvnd.common.OsUtils;
/**
* A terminal {@link ClientOutput} based on JLine.
@@ -62,6 +66,7 @@ public class TerminalOutput implements ClientOutput {
private final Terminal terminal;
private final Terminal.SignalHandler previousIntHandler;
private final Display display;
private final Map<String, Map<String, TransferEvent>> transfers = new LinkedHashMap<>();
private final LinkedHashMap<String, Project> projects = new LinkedHashMap<>();
private final ClientLog log;
private final Thread reader;
@@ -353,6 +358,22 @@ public class TerminalOutput implements ClientOutput {
}
break;
}
case Message.TRANSFER_INITIATED:
case Message.TRANSFER_STARTED:
case Message.TRANSFER_PROGRESSED: {
final TransferEvent te = (TransferEvent) entry;
transfers.computeIfAbsent(orEmpty(te.getProjectId()), p -> new LinkedHashMap<>())
.put(te.getResourceName(), te);
break;
}
case Message.TRANSFER_CORRUPTED:
case Message.TRANSFER_SUCCEEDED:
case Message.TRANSFER_FAILED: {
final TransferEvent te = (TransferEvent) entry;
transfers.computeIfAbsent(orEmpty(te.getProjectId()), p -> new LinkedHashMap<>())
.remove(te.getResourceName());
break;
}
default:
throw new IllegalStateException("Unexpected message " + entry);
}
@@ -360,6 +381,10 @@ public class TerminalOutput implements ClientOutput {
return true;
}
private String orEmpty(String s) {
return s != null ? s : "";
}
private void applyNoBuffering() {
projects.values().stream().flatMap(p -> p.log.stream()).forEach(log);
projects.clear();
@@ -454,12 +479,22 @@ public class TerminalOutput implements ClientOutput {
return;
}
final List<AttributedString> lines = new ArrayList<>(rows);
int dispLines = rows - 1; // for the build status line
dispLines--; // there's a bug which sometimes make the cursor goes one line below, so keep one more line empty
// at the end
final int projectsCount = projects.size();
int dispLines = rows;
// status line
dispLines--;
// there's a bug which sometimes make the cursor goes one line below,
// so keep one more line empty at the end
dispLines--;
AttributedString globalTransfer = formatTransfers("");
dispLines -= globalTransfer != null ? 1 : 0;
addStatusLine(lines, dispLines, projectsCount);
if (globalTransfer != null) {
lines.add(globalTransfer);
}
if (projectsCount <= dispLines) {
int remLogLines = dispLines - projectsCount;
@@ -490,6 +525,74 @@ public class TerminalOutput implements ClientOutput {
display.update(trimmed, -1);
}
private AttributedString formatTransfers(String projectId) {
Collection<TransferEvent> transfers = this.transfers.getOrDefault(projectId, Collections.emptyMap()).values();
if (transfers.isEmpty()) {
return null;
}
TransferEvent event = transfers.iterator().next();
String action = event.getRequest() == TransferEvent.PUT ? "Uploading" : "Downloading";
if (transfers.size() == 1) {
String direction = event.getRequest() == TransferEvent.PUT ? "to" : "from";
long cur = event.getTransferredBytes();
long max = event.getContentLength();
String prg = OsUtils.kbTohumanReadable(cur / 1024) + " / " + OsUtils.kbTohumanReadable(max / 1024);
AttributedStringBuilder asb = new AttributedStringBuilder();
asb.append(action);
asb.append(" ");
asb.style(AttributedStyle.BOLD);
asb.append(pathToMaven(event.getResourceName()));
asb.style(AttributedStyle.DEFAULT);
asb.append(" ");
asb.append(direction);
asb.append(" ");
asb.append(event.getRepositoryId());
if (cur > 0 && cur < max) {
asb.append(": ");
asb.append(prg);
}
return asb.toAttributedString();
} else {
return new AttributedString(action + " " + transfers.size() + " files...");
}
}
public static String pathToMaven(String location) {
String[] p = location.split("/");
if (p.length >= 4 && p[p.length - 1].startsWith(p[p.length - 3] + "-" + p[p.length - 2])) {
String artifactId = p[p.length - 3];
String version = p[p.length - 2];
String classifier;
String type;
String artifactIdVersion = artifactId + "-" + version;
StringBuilder sb = new StringBuilder();
if (p[p.length - 1].charAt(artifactIdVersion.length()) == '-') {
classifier = p[p.length - 1].substring(artifactIdVersion.length() + 1, p[p.length - 1].lastIndexOf('.'));
} else {
classifier = null;
}
type = p[p.length - 1].substring(p[p.length - 1].lastIndexOf('.') + 1);
for (int j = 0; j < p.length - 3; j++) {
if (j > 0) {
sb.append('.');
}
sb.append(p[j]);
}
sb.append(':').append(artifactId).append(':').append(version);
if (!"jar".equals(type) || classifier != null) {
sb.append(':');
if (!"jar".equals(type)) {
sb.append(type);
}
if (classifier != null) {
sb.append(':').append(classifier);
}
}
return sb.toString();
}
return location;
}
private void addStatusLine(final List<AttributedString> lines, int dispLines, final int projectsCount) {
if (name != null || buildStatus != null) {
AttributedStringBuilder asb = new AttributedStringBuilder();
@@ -548,7 +651,13 @@ public class TerminalOutput implements ClientOutput {
private void addProjectLine(final List<AttributedString> lines, Project prj) {
final MojoStartedEvent execution = prj.runningExecution;
final AttributedStringBuilder asb = new AttributedStringBuilder();
if (execution == null) {
AttributedString transfer = formatTransfers(prj.id);
if (transfer != null) {
asb
.append(':')
.append(String.format(artifactIdFormat, prj.id))
.append(transfer);
} else if (execution == null) {
asb
.append(':')
.append(prj.id);