This commit is contained in:
Richard Willis 2020-02-15 13:35:41 +01:00
Родитель 666b385bef
Коммит aa6b30b3e8
34 изменённых файлов: 567 добавлений и 568 удалений

4
.gitignore поставляемый
Просмотреть файл

@ -9,5 +9,7 @@ lib/**
.settings
.idea
.scannerwork
// These files will be generated from the i18n folder
# These files will be generated from the i18n folder
*nls.*.json
# Generated JS from protos
/generated

Просмотреть файл

@ -6,12 +6,13 @@ Start by opening an issue using one of the issue templates, or propose a change
## Running the project
1. Make sure you have [nvm](https://github.com/nvm-sh/nvm) installed
2. Change directory to the root of the project
3. Select Node version with `nvm use`
4. Install Node packages: `npm install`
5. Install [Java version >= 8](https://adoptopenjdk.net/)
6. Build Java executables: `npm run compile:java`
1. Install [nvm](https://github.com/nvm-sh/nvm) installed
2. Install protobuf tools: `brew install protobuf`
3. Install [Java version >= 8](https://adoptopenjdk.net/)
4. Change directory to the root of the project
5. Select Node version with `nvm use`
6. Install Node packages: `npm install`
7. Build Java executables: `npm run compile:java`
Now open the root of the project in VS Code, click on the Debug panel, and select either "Run Extension" or any of the test launch configurations.

12
compile-protos.sh Executable file
Просмотреть файл

@ -0,0 +1,12 @@
#!/usr/bin/env bash
PROTOC_GEN_TS_PATH="./node_modules/.bin/protoc-gen-ts"
OUT_DIR="./generated"
mkdir -p "$OUT_DIR"
protoc \
--plugin="protoc-gen-ts=${PROTOC_GEN_TS_PATH}" \
--js_out="import_style=commonjs,binary:${OUT_DIR}" \
--ts_out="${OUT_DIR}" \
proto/com/github/badsyntax/gradletasks/*.proto

2
java-gradle-tasks/.vscode/launch.json поставляемый
Просмотреть файл

@ -9,7 +9,7 @@
"name": "CodeLens (Launch) - Application",
"request": "launch",
"mainClass": "com.github.badsyntax.gradletasks.Application",
"projectName": "gradle-tasks"
"projectName": "gradle-tasks-server"
}
]
}

Просмотреть файл

@ -3,6 +3,7 @@ plugins {
id 'application'
id 'jacoco'
id 'com.github.johnrengelman.shadow' version '5.2.0'
id "com.google.protobuf" version "0.8.11"
}
repositories {
@ -14,19 +15,44 @@ repositories {
sourceSets {
libsDirName = 'lib'
main {
proto {
srcDir '../proto'
}
}
generated {
java.srcDir "${buildDir}/generated/sources/annotationProcessor/java/main"
java.srcDirs = [
"${buildDir}/generated/sources/annotationProcessor/java/main", // dagger
"${buildDir}/generated/source/proto/main/java" // protobuf
]
}
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.11.3'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
dependencies {
compile 'com.google.protobuf:protobuf-javalite:3.11.3'
compile 'org.gradle:gradle-tooling-api:6.1.1'
compile 'com.eclipsesource.minimal-json:minimal-json:0.9.5'
compile "org.java-websocket:Java-WebSocket:1.4.0"
compile "com.google.dagger:dagger:2.26"
annotationProcessor "com.google.dagger:dagger-compiler:2.26"
runtimeOnly 'org.slf4j:slf4j-simple:1.7.30'
testImplementation 'junit:junit:4.13'
implementation 'com.google.protobuf:protobuf-gradle-plugin:0.8.11'
}
application {

Просмотреть файл

@ -8,6 +8,7 @@ public class Application {
@Inject
Server server;
@Inject
public Application(int port) {
ApplicationFactory applicationFactory =
DaggerApplicationFactory.builder().withPort(port).build();
@ -15,7 +16,7 @@ public class Application {
}
public static void main(String[] args) throws ApplicationException {
int port = 8887;
int port = 2222;
if (args.length > 0) {
try {
port = Integer.parseInt(args[0]);

Просмотреть файл

@ -1,8 +1,10 @@
package com.github.badsyntax.gradletasks;
import javax.inject.Singleton;
import dagger.BindsInstance;
import dagger.Component;
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationFactory {
Application get();

Просмотреть файл

@ -1,9 +1,12 @@
package com.github.badsyntax.gradletasks;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;
import javax.inject.Singleton;
import com.github.badsyntax.gradletasks.logging.BasicWriteFormatter;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import dagger.Module;
@ -14,6 +17,7 @@ public class ApplicationModule {
private ApplicationModule() {
}
@Singleton
@Provides
static Logger provideLogger() {
StreamHandler logHandler = new ConsoleHandler();
@ -31,4 +35,9 @@ public class ApplicationModule {
static GradleTaskPool provideTaskPool() {
return new GradleTaskPool();
}
@Provides
static ExecutorService provideExecutorService() {
return Executors.newCachedThreadPool();
}
}

Просмотреть файл

@ -2,19 +2,14 @@ package com.github.badsyntax.gradletasks.server;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import javax.inject.Inject;
import com.eclipsesource.json.JsonObject;
import javax.inject.Singleton;
import com.github.badsyntax.gradletasks.messages.client.ClientMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import com.github.badsyntax.gradletasks.server.actions.ActionRunner;
import com.github.badsyntax.gradletasks.server.actions.GetTasksAction;
import com.github.badsyntax.gradletasks.server.actions.RunTaskAction;
import com.github.badsyntax.gradletasks.server.actions.StopGetTasksAction;
import com.github.badsyntax.gradletasks.server.actions.StopTaskAction;
import com.github.badsyntax.gradletasks.server.messages.ClientMessage;
import com.github.badsyntax.gradletasks.server.messages.GenericErrorMessage;
import com.github.badsyntax.gradletasks.server.messages.GenericMessage;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionRunnerException;
import com.google.protobuf.InvalidProtocolBufferException;
import org.gradle.tooling.CancellationTokenSource;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
@ -22,25 +17,31 @@ import org.java_websocket.server.WebSocketServer;
public class Server extends WebSocketServer {
private final ExecutorService taskExecutor = Executors.newCachedThreadPool();
@Singleton
private Logger logger;
@Singleton
private GradleTaskPool taskPool;
private static final String MESSAGE_TYPE_KEY = "type";
@Singleton
protected ActionRunner actionRunner;
@Inject
public Server(Logger logger, GradleTaskPool taskPool, int port) {
public Server(Logger logger, GradleTaskPool taskPool, int port, ActionRunner actionRunner) {
super(new InetSocketAddress(port));
this.logger = logger;
this.taskPool = taskPool;
this.actionRunner = actionRunner;
}
@Override
public void onOpen(WebSocket connection, ClientHandshake handshake) {
String localHostAddress = connection.getLocalSocketAddress().getAddress().getHostAddress();
int localPort = connection.getLocalSocketAddress().getPort();
connection.send(new GenericMessage(
String.format("Connected to %s:%d. Welcome client!", localHostAddress, localPort))
.toString());
connection.send(ServerMessage.Message.newBuilder()
.setInfo(ServerMessage.Info.newBuilder().setMessage(String.format(
"Connected to %s:%d. Welcome client!", localHostAddress, localPort)))
.build().toByteArray());
}
@Override
@ -53,47 +54,33 @@ public class Server extends WebSocketServer {
}));
}
@Override
public void onMessage(WebSocket connection, String message) {
handleMessageAction(connection, new ClientMessage(message));
}
@Override
public void onMessage(WebSocket connection, ByteBuffer message) {
handleMessageAction(connection, new ClientMessage(new String(message.array())));
try {
handleMessageAction(connection, ClientMessage.Message.parseFrom(message));
} catch (InvalidProtocolBufferException e) {
logError(connection, e.getMessage());
}
}
private void handleMessageAction(WebSocket connection, ClientMessage clientMessage) {
JsonObject message = clientMessage.getMessage();
String messageType = message.get(MESSAGE_TYPE_KEY).asString();
ActionRunner actionRunner =
new ActionRunner(connection, message, taskExecutor, logger, taskPool);
switch (messageType) {
case RunTaskAction.KEY:
actionRunner.runTask();
break;
case GetTasksAction.KEY:
actionRunner.getTasks();
break;
case StopTaskAction.KEY:
actionRunner.stopTask();
break;
case StopGetTasksAction.KEY:
actionRunner.stopGetTasks();
break;
default:
logError(connection, String.format("Unknown action: %s", messageType));
private void handleMessageAction(WebSocket connection, ClientMessage.Message clientMessage) {
try {
actionRunner.run(connection, clientMessage);
} catch (ActionRunnerException e) {
logError(connection, e.getMessage());
}
}
private void logError(WebSocket connection, String errorMessage) {
connection.send(new GenericErrorMessage(errorMessage).toString());
connection.send(ServerMessage.Message.newBuilder()
.setError(ServerMessage.Error.newBuilder().setMessage(errorMessage)).build()
.toByteArray());
logger.warning(errorMessage);
}
@Override
public void onError(WebSocket connection, Exception e) {
if (connection != null) {
if (connection != null && e.getMessage() != null) {
logError(connection, e.getMessage());
}
}
@ -104,4 +91,9 @@ public class Server extends WebSocketServer {
setConnectionLostTimeout(0);
setConnectionLostTimeout(100);
}
@Override
public void onMessage(WebSocket conn, String message) {
// TODO Auto-generated method stub
}
}

Просмотреть файл

@ -2,56 +2,34 @@ package com.github.badsyntax.gradletasks.server.actions;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.eclipsesource.json.JsonObject;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import com.github.badsyntax.gradletasks.server.listeners.GradleOutputListener;
import com.github.badsyntax.gradletasks.server.listeners.GradleProgressListener;
import com.github.badsyntax.gradletasks.server.messages.GenericErrorMessage;
import org.java_websocket.WebSocket;
abstract class Action {
protected static final String MESSAGE_SOURCE_DIR_KEY = "sourceDir";
protected static final String MESSAGE_TYPE_KEY = "type";
protected static final String MESSAGE_TASK_KEY = "task";
protected static final String MESSAGE_TASK_ARGS_KEY = "args";
protected WebSocket connection;
protected JsonObject message;
class Action {
@Singleton
protected ExecutorService taskExecutor;
@Singleton
protected GradleTaskPool taskPool;
@Singleton
protected Logger logger;
protected GradleProgressListener progressListener;
protected GradleOutputListener stdOutListener;
protected GradleOutputListener stdErrListener;
public Action(WebSocket connection, JsonObject message, ExecutorService taskExecutor, Logger logger, GradleTaskPool taskPool) {
this.connection = connection;
this.message = message;
this.taskExecutor = taskExecutor;
@Inject
public Action(Logger logger, ExecutorService taskExecutor, GradleTaskPool taskPool) {
this.logger = logger;
this.taskExecutor = taskExecutor;
this.taskPool = taskPool;
this.progressListener = new GradleProgressListener(connection);
this.stdOutListener = new GradleOutputListener(connection, GradleOutputListener.TYPES.STDOUT);
this.stdErrListener = new GradleOutputListener(connection, GradleOutputListener.TYPES.STDERR);
}
public GradleProgressListener getProgressListener() {
return progressListener;
}
public GradleOutputListener getStdOutListener() {
return stdOutListener;
}
public GradleOutputListener getStdErrListener() {
return stdErrListener;
}
protected void logError(String error) {
if (connection.isOpen()) {
connection.send(new GenericErrorMessage(error).toString());
}
protected void logError(WebSocket connection, String error) {
logger.warning(error);
if (connection.isOpen()) {
connection
.send(ServerMessage.Error.newBuilder().setMessage(error).build().toByteArray());
}
}
}

Просмотреть файл

@ -1,41 +1,44 @@
package com.github.badsyntax.gradletasks.server.actions;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.eclipsesource.json.JsonObject;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.github.badsyntax.gradletasks.messages.client.ClientMessage;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionRunnerException;
import org.java_websocket.WebSocket;
public class ActionRunner {
protected WebSocket connection;
protected JsonObject message;
protected ExecutorService taskExecutor;
protected GradleTaskPool taskPool;
protected Logger logger;
@Singleton
private RunTaskAction runTaskAction;
public ActionRunner(WebSocket connection, JsonObject message, ExecutorService taskExecutor,
Logger logger, GradleTaskPool taskPool) {
this.connection = connection;
this.message = message;
this.taskExecutor = taskExecutor;
this.logger = logger;
this.taskPool = taskPool;
@Singleton
private GetTasksAction getTasksAction;
@Singleton
private StopTaskAction stopTaskAction;
@Singleton
private StopGetTasksAction stopGetTasksAction;
@Inject
public ActionRunner(RunTaskAction runTaskAction, GetTasksAction getTasksAction, StopTaskAction stopTaskAction, StopGetTasksAction stopGetTasksAction) {
this.runTaskAction = runTaskAction;
this.getTasksAction = getTasksAction;
this.stopTaskAction = stopTaskAction;
this.stopGetTasksAction = stopGetTasksAction;
}
public void runTask() {
new RunTaskAction(connection, message, taskExecutor, logger, taskPool).run();
}
public void getTasks() {
new GetTasksAction(connection, message, taskExecutor, logger, taskPool).run();
}
public void stopTask() {
new StopTaskAction(connection, message, taskExecutor, logger, taskPool).run();
}
public void stopGetTasks() {
new StopGetTasksAction(connection, message, taskExecutor, logger, taskPool).run();
public void run(WebSocket connection, ClientMessage.Message message) throws ActionRunnerException {
if (message.hasGetTasks()){
getTasksAction.run(connection, message.getGetTasks());
} else if (message.hasRunTask()) {
runTaskAction.run(connection, message.getRunTask());
} else if (message.hasStopTask()) {
stopTaskAction.run(connection, message.getStopTask());
} else if (message.hasStopGetTasks()) {
stopGetTasksAction.run(connection, message.getStopGetTasks());
} else {
throw new ActionRunnerException("Unknown client message");
}
}
}

Просмотреть файл

@ -1,13 +1,17 @@
package com.github.badsyntax.gradletasks.server.actions;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import javax.inject.Inject;
import com.github.badsyntax.gradletasks.messages.client.ClientMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import com.github.badsyntax.gradletasks.server.messages.TasksMessage;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionException;
import com.github.badsyntax.gradletasks.server.listeners.GradleOutputListener;
import com.github.badsyntax.gradletasks.server.listeners.GradleProgressListener;
import org.gradle.tooling.CancellationTokenSource;
import org.gradle.tooling.GradleConnector;
import org.gradle.tooling.ModelBuilder;
@ -16,77 +20,86 @@ import org.gradle.tooling.model.GradleProject;
import org.java_websocket.WebSocket;
public class GetTasksAction extends Action {
private JsonArray jsonTasks = Json.array();
public static final String KEY = "getTasks";
public GetTasksAction(WebSocket connection, JsonObject message, ExecutorService taskExecutor,
Logger logger, GradleTaskPool taskPool) {
super(connection, message, taskExecutor, logger, taskPool);
@Inject
public GetTasksAction(final Logger logger, final ExecutorService taskExecutor,
final GradleTaskPool taskPool) {
super(logger, taskExecutor, taskPool);
}
public JsonArray getJsonTasks() {
return jsonTasks;
}
private final List<ServerMessage.GradleTask> tasks = new ArrayList<ServerMessage.GradleTask>();
public static final String KEY = "ACTION_GET_TASKS";
public static String getTaskKey(File sourceDir) {
public static String getTaskKey(final File sourceDir) {
return KEY + sourceDir.getAbsolutePath();
}
public void run() {
public void run(final WebSocket connection, final ClientMessage.GetTasks message) {
taskExecutor.submit(() -> {
try {
File sourceDir = new File(message.get(MESSAGE_SOURCE_DIR_KEY).asString());
final File sourceDir = new File(message.getSourceDir().trim());
if (!sourceDir.exists()) {
throw new ActionException("Source directory does not exist");
}
getTasks(sourceDir);
} catch (Exception e) {
logError(e.getMessage());
getTasks(connection, sourceDir);
} catch (final Exception e) {
logError(connection, e.getMessage());
} finally {
if (connection.isOpen()) {
connection.send(
new TasksMessage(String.format("Completed %s action", KEY), jsonTasks)
.toString());
connection.send(ServerMessage.Message.newBuilder()
.setGetTasks(ServerMessage.Tasks.newBuilder()
.setMessage(String.format("Completed %s action", KEY))
.addAllTasks(tasks))
.build().toByteArray());
}
}
});
}
private void getTasks(File sourceDir) throws ActionException {
ProjectConnection projectConnection =
private void getTasks(final WebSocket connection, final File sourceDir) throws ActionException {
final ProjectConnection projectConnection =
GradleConnector.newConnector().forProjectDirectory(sourceDir).connect();
CancellationTokenSource cancellationTokenSource =
final CancellationTokenSource cancellationTokenSource =
GradleConnector.newCancellationTokenSource();
taskPool.put(getTaskKey(sourceDir), cancellationTokenSource, GradleTaskPool.TYPE.GET);
try {
ModelBuilder<GradleProject> rootProjectBuilder =
final ModelBuilder<GradleProject> rootProjectBuilder =
projectConnection.model(GradleProject.class);
rootProjectBuilder.withCancellationToken(cancellationTokenSource.token());
rootProjectBuilder.addProgressListener(progressListener);
rootProjectBuilder.setStandardOutput(stdOutListener);
rootProjectBuilder.setStandardError(stdErrListener);
rootProjectBuilder.addProgressListener(new GradleProgressListener(connection));
rootProjectBuilder.setStandardOutput(new GradleOutputListener(connection,
ServerMessage.OutputChanged.OutputType.STDOUT));
rootProjectBuilder.setStandardError(new GradleOutputListener(connection,
ServerMessage.OutputChanged.OutputType.STDERR));
rootProjectBuilder.setColorOutput(false);
GradleProject rootProject = rootProjectBuilder.get();
final GradleProject rootProject = rootProjectBuilder.get();
tasks.clear();
buildTasksListFromProjectTree(rootProject);
} catch (Exception err) {
} catch (final Exception err) {
throw new ActionException(err.getMessage());
} finally {
projectConnection.close();
}
}
private void buildTasksListFromProjectTree(GradleProject project) {
private void buildTasksListFromProjectTree(final GradleProject project) {
buildTasksListFromProjectTree(project, project);
}
private void buildTasksListFromProjectTree(GradleProject project, GradleProject rootProject) {
project.getTasks().stream().map(task -> Json.object().add("name", task.getName())
.add("group", task.getGroup()).add("path", task.getPath())
.add("project", task.getProject().getName())
.add("buildFile",
task.getProject().getBuildScript().getSourceFile().getAbsolutePath())
.add("rootProject", rootProject.getName())
.add("description", task.getDescription())).forEach(jsonTasks::add);
private void buildTasksListFromProjectTree(final GradleProject project,
final GradleProject rootProject) {
project.getTasks().stream().forEach(task -> {
ServerMessage.GradleTask.Builder gradleTask = ServerMessage.GradleTask.newBuilder()
.setProject(task.getProject().getName()).setName(task.getName())
.setPath(task.getPath())
.setBuildFile(
task.getProject().getBuildScript().getSourceFile().getAbsolutePath())
.setRootProject(rootProject.getName()).setDescription(task.getDescription());
if (task.getGroup() != null) {
gradleTask.setGroup(task.getGroup());
}
tasks.add(gradleTask.build());
});
project.getChildren().stream()
.forEach(childProject -> buildTasksListFromProjectTree(childProject, rootProject));
}

Просмотреть файл

@ -1,13 +1,18 @@
package com.github.badsyntax.gradletasks.server.actions;
import java.io.File;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
import javax.inject.Inject;
import com.github.badsyntax.gradletasks.messages.client.ClientMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import com.github.badsyntax.gradletasks.server.messages.ActionCancelledMessage;
import com.github.badsyntax.gradletasks.server.messages.RunTaskMessage;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionCancelledException;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionException;
import com.github.badsyntax.gradletasks.server.listeners.GradleOutputListener;
import com.github.badsyntax.gradletasks.server.listeners.GradleProgressListener;
import com.google.protobuf.ProtocolStringList;
import org.gradle.tooling.BuildCancelledException;
import org.gradle.tooling.BuildLauncher;
import org.gradle.tooling.CancellationTokenSource;
@ -17,46 +22,44 @@ import org.java_websocket.WebSocket;
public class RunTaskAction extends Action {
public static final String KEY = "runTask";
public RunTaskAction(WebSocket connection, JsonObject message, ExecutorService taskExecutor,
Logger logger, GradleTaskPool taskPool) {
super(connection, message, taskExecutor, logger, taskPool);
@Inject
public RunTaskAction(Logger logger, ExecutorService taskExecutor, GradleTaskPool taskPool) {
super(logger, taskExecutor, taskPool);
// TODO Auto-generated constructor stub
}
public static final String KEY = "ACTION_RUN_TASK";
public static String getTaskKey(File sourceDir, String task) {
return KEY + sourceDir.getAbsolutePath() + task;
}
public void run() {
public void run(WebSocket connection, ClientMessage.RunTask message) {
taskExecutor.submit(() -> {
String task = "";
String sourceDirStr = "";
try {
task = message.get(MESSAGE_TASK_KEY).asString();
sourceDirStr = message.get(MESSAGE_SOURCE_DIR_KEY).asString();
File sourceDir = new File(sourceDirStr);
String[] args = message.get(MESSAGE_TASK_ARGS_KEY).asArray().values().stream()
.map(JsonValue::asString).toArray(String[]::new);
if (!sourceDir.exists()) {
throw new ActionException("Source directory does not exist");
}
runTask(sourceDir, task, args);
File sourceDir = new File(message.getSourceDir().trim());
runTask(connection, sourceDir, message.getTask(), message.getArgsList());
} catch (ActionCancelledException e) {
connection.send(new ActionCancelledMessage(e.getMessage(), task, sourceDirStr).toString());
connection.send(ServerMessage.Message.newBuilder()
.setActionCancelled(ServerMessage.ActionCancelled.newBuilder()
.setMessage(e.getMessage()).setTask(message.getTask())
.setSourceDir(message.getSourceDir().trim()))
.build().toByteArray());
} catch (ActionException e) {
logError(e.getMessage());
logError(connection, e.getMessage());
} finally {
if (connection.isOpen()) {
connection.send(
new RunTaskMessage(String.format("Completed %s action", KEY), task)
.toString());
connection.send(ServerMessage.Message.newBuilder()
.setRunTask(ServerMessage.RunTask.newBuilder()
.setMessage(String.format("Completed %s action", KEY))
.setTask(message.getTask()))
.build().toByteArray());
}
}
});
}
private void runTask(File sourceDir, String task, String[] args)
private void runTask(WebSocket connection, File sourceDir, String task, List<String> args)
throws ActionException, ActionCancelledException {
ProjectConnection projectConnection =
GradleConnector.newConnector().forProjectDirectory(sourceDir).connect();
@ -67,9 +70,11 @@ public class RunTaskAction extends Action {
GradleTaskPool.TYPE.RUN);
BuildLauncher build = projectConnection.newBuild();
build.withCancellationToken(cancellationTokenSource.token());
build.addProgressListener(progressListener);
build.setStandardOutput(stdOutListener);
build.setStandardError(stdErrListener);
build.addProgressListener(new GradleProgressListener(connection));
build.setStandardOutput(new GradleOutputListener(connection,
ServerMessage.OutputChanged.OutputType.STDOUT));
build.setStandardError(new GradleOutputListener(connection,
ServerMessage.OutputChanged.OutputType.STDERR));
build.setColorOutput(true);
build.withArguments(args);
build.forTasks(task);

Просмотреть файл

@ -4,24 +4,28 @@ import java.io.File;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.eclipsesource.json.JsonObject;
import javax.inject.Inject;
import com.github.badsyntax.gradletasks.messages.client.ClientMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import com.github.badsyntax.gradletasks.server.messages.GenericMessage;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionException;
import org.gradle.tooling.CancellationTokenSource;
import org.java_websocket.WebSocket;
public class StopGetTasksAction extends Action {
public static final String KEY = "stopGetTasks";
public StopGetTasksAction(WebSocket connection, JsonObject message,
ExecutorService taskExecutor, Logger logger, GradleTaskPool taskPool) {
super(connection, message, taskExecutor, logger, taskPool);
@Inject
public StopGetTasksAction(Logger logger, ExecutorService taskExecutor,
GradleTaskPool taskPool) {
super(logger, taskExecutor, taskPool);
// TODO Auto-generated constructor stub
}
public void run() {
public static final String KEY = "ACTION_STOP_GET_TASKS";
public void run(WebSocket connection, ClientMessage.StopGetTasks message) {
try {
File sourceDir = new File(message.get(MESSAGE_SOURCE_DIR_KEY).asString());
File sourceDir = new File(message.getSourceDir().trim());
if (!sourceDir.getPath().equals("")) {
if (sourceDir.getAbsolutePath() != null && !sourceDir.exists()) {
throw new ActionException("Source directory does not exist");
@ -43,11 +47,13 @@ public class StopGetTasksAction extends Action {
});
}
} catch (ActionException e) {
logError(e.getMessage());
logError(connection, e.getMessage());
} finally {
if (connection.isOpen()) {
connection.send(
new GenericMessage(String.format("Completed %s action", KEY)).toString());
connection.send(ServerMessage.Message.newBuilder()
.setInfo(ServerMessage.Info.newBuilder()
.setMessage(String.format("Completed %s action", KEY)))
.build().toByteArray());
}
}
}

Просмотреть файл

@ -3,40 +3,45 @@ package com.github.badsyntax.gradletasks.server.actions;
import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.eclipsesource.json.JsonObject;
import javax.inject.Inject;
import com.github.badsyntax.gradletasks.messages.client.ClientMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import com.github.badsyntax.gradletasks.server.GradleTaskPool;
import com.github.badsyntax.gradletasks.server.messages.GenericMessage;
import com.github.badsyntax.gradletasks.server.actions.exceptions.ActionException;
import org.gradle.tooling.CancellationTokenSource;
import org.java_websocket.WebSocket;
public class StopTaskAction extends Action {
public static final String KEY = "stopTask";
public StopTaskAction(WebSocket connection, JsonObject message, ExecutorService taskExecutor,
Logger logger, GradleTaskPool taskPool) {
super(connection, message, taskExecutor, logger, taskPool);
@Inject
public StopTaskAction(Logger logger, ExecutorService taskExecutor, GradleTaskPool taskPool) {
super(logger, taskExecutor, taskPool);
// TODO Auto-generated constructor stub
}
public void run() {
public static final String KEY = "ACTION_STOP_TASK";
public void run(WebSocket connection, ClientMessage.StopTask message) {
try {
String task = message.get(MESSAGE_TASK_KEY).asString();
File sourceDir = new File(message.get(MESSAGE_SOURCE_DIR_KEY).asString());
File sourceDir = new File(message.getSourceDir().trim());
if (!sourceDir.exists()) {
throw new ActionException("Source directory does not exist");
}
String key = RunTaskAction.getTaskKey(sourceDir, task);
CancellationTokenSource cancellationTokenSource = taskPool.get(key, GradleTaskPool.TYPE.RUN);
String key = RunTaskAction.getTaskKey(sourceDir, message.getTask());
CancellationTokenSource cancellationTokenSource =
taskPool.get(key, GradleTaskPool.TYPE.RUN);
if (cancellationTokenSource != null) {
cancellationTokenSource.cancel();
taskPool.remove(key, GradleTaskPool.TYPE.RUN);
}
} catch (ActionException e) {
logError(e.getMessage());
logError(connection, e.getMessage());
} finally {
if (connection.isOpen()) {
connection
.send(new GenericMessage(String.format("Completed %s action", KEY)).toString());
connection.send(ServerMessage.Message.newBuilder()
.setInfo(ServerMessage.Info.newBuilder()
.setMessage(String.format("Completed %s action", KEY)))
.build().toByteArray());
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
package com.github.badsyntax.gradletasks.server.actions;
package com.github.badsyntax.gradletasks.server.actions.exceptions;
public class ActionCancelledException extends Exception {
public ActionCancelledException(String message) {

Просмотреть файл

@ -1,4 +1,4 @@
package com.github.badsyntax.gradletasks.server.actions;
package com.github.badsyntax.gradletasks.server.actions.exceptions;
public class ActionException extends Exception {
public ActionException(String message) {

Просмотреть файл

@ -0,0 +1,11 @@
package com.github.badsyntax.gradletasks.server.actions.exceptions;
public class ActionRunnerException extends Exception {
public ActionRunnerException(String message) {
super(message);
}
public ActionRunnerException(String message, Throwable cause) {
super(message, cause);
}
}

Просмотреть файл

@ -4,29 +4,28 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.inject.Inject;
import com.github.badsyntax.gradletasks.server.messages.OutputChangedMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import org.java_websocket.WebSocket;
public class GradleOutputListener extends OutputStream {
public enum TYPES {
STDERR, STDOUT,
}
private WebSocket connection;
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
private String typeString;
private ServerMessage.OutputChanged.OutputType outputType;
@Inject
public GradleOutputListener(WebSocket connection, TYPES type) {
public GradleOutputListener(WebSocket connection, ServerMessage.OutputChanged.OutputType outputType) {
this.connection = connection;
this.typeString = type.toString();
this.outputType = outputType;
}
@Override
public final void write(int b) throws IOException {
char c = (char) b;
if (c == System.lineSeparator().charAt(0)) {
connection.send(new OutputChangedMessage(baos.toString(), typeString).toString());
connection.send(ServerMessage.Message.newBuilder()
.setOutputChanged(ServerMessage.OutputChanged.newBuilder()
.setMessage(baos.toString()).setOutputType(outputType))
.build().toByteArray());
baos.reset();
} else {
baos.write(b);

Просмотреть файл

@ -1,6 +1,6 @@
package com.github.badsyntax.gradletasks.server.listeners;
import com.github.badsyntax.gradletasks.server.messages.ProgressMessage;
import com.github.badsyntax.gradletasks.messages.server.ServerMessage;
import org.gradle.tooling.ProgressEvent;
import org.gradle.tooling.ProgressListener;
import org.java_websocket.WebSocket;
@ -14,6 +14,8 @@ public class GradleProgressListener implements ProgressListener {
@Override
public void statusChanged(ProgressEvent progressEvent) {
connection.send(new ProgressMessage(progressEvent.getDescription()).toString());
connection.send(ServerMessage.Message.newBuilder().setProgress(
ServerMessage.Progress.newBuilder().setMessage(progressEvent.getDescription()))
.build().toByteArray());
}
}

Просмотреть файл

@ -1,23 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
public class ActionCancelledMessage extends GenericMessage {
private static final String TYPE = "ACTION_CANCELLED";
private String task;
private String sourceDir;
public ActionCancelledMessage(String message, String task, String sourceDir) {
super(message, TYPE);
this.task = task;
this.sourceDir = sourceDir;
}
@Override
public String toString() {
return Json.object().add("type", type).add("message", message).add("task", task)
.add("sourceDir", sourceDir).toString();
}
}

Просмотреть файл

@ -1,17 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonObject;
public class ClientMessage {
private JsonObject messageObject;
public ClientMessage(String message) {
this.messageObject = Json.parse(message).asObject();
}
public JsonObject getMessage() {
return this.messageObject;
}
}

Просмотреть файл

@ -1,10 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
public class GenericErrorMessage extends GenericMessage {
private static final String TYPE = "ERROR";
public GenericErrorMessage(String message) {
super(message, TYPE);
}
}

Просмотреть файл

@ -1,22 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
public class GenericMessage {
protected String message;
protected String type;
public GenericMessage(String message, String type) {
this.message = message;
this.type = type;
}
public GenericMessage(String message) {
this(message, "GENERIC_MESSAGE");
}
public String toString() {
return Json.object().add("type", type).add("message", message).toString();
}
}

Просмотреть файл

@ -1,20 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
public class OutputChangedMessage {
String message;
String outputType;
private static final String TYPE = "GRADLE_OUTPUT";
public OutputChangedMessage(String message, String outputType) {
this.message = message;
this.outputType = outputType;
}
public String toString() {
return Json.object().add("type", TYPE).add("message", message)
.add("outputType", outputType).toString();
}
}

Просмотреть файл

@ -1,17 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
public class ProgressMessage {
String message;
private static final String TYPE = "GRADLE_PROGRESS";
public ProgressMessage(String message) {
this.message = message;
}
public String toString() {
return Json.object().add("type", TYPE).add("message", message).toString();
}
}

Просмотреть файл

@ -1,19 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
public class RunTaskMessage extends GenericMessage {
private String task;
private static final String TYPE = "GRADLE_RUN_TASK";
public RunTaskMessage(String message, String task) {
super(message, TYPE);
this.task = task;
}
@Override
public String toString() {
return Json.object().add("type", type).add("message", message).add("task", task).toString();
}
}

Просмотреть файл

@ -1,19 +0,0 @@
package com.github.badsyntax.gradletasks.server.messages;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray;
public class TasksMessage extends GenericMessage {
JsonArray tasks;
private static final String TYPE = "GRADLE_TASKS";
public TasksMessage(String message, JsonArray tasks) {
super(message);
this.tasks = tasks;
}
public String toString() {
return Json.object().add("type", TYPE).add("tasks", tasks).toString();
}
}

19
package-lock.json сгенерированный
Просмотреть файл

@ -146,6 +146,12 @@
"@types/node": "*"
}
},
"@types/google-protobuf": {
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.2.tgz",
"integrity": "sha512-ifFemzjNchFBCtHS6bZNhSZCBu7tbtOe0e8qY0z2J4HtFXmPJjm6fXSaQsTG7yhShBEZtt2oP/bkwu5k+emlkQ==",
"dev": true
},
"@types/json-schema": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
@ -2895,6 +2901,11 @@
"sparkles": "^1.0.0"
}
},
"google-protobuf": {
"version": "3.11.4",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.11.4.tgz",
"integrity": "sha512-lL6b04rDirurUBOgsY2+LalI6Evq8eH5TcNzi7TYQ3BsIWelT0KSOQSBsXuavEkNf+odQU6c0lgz3UsZXeNX9Q=="
},
"graceful-fs": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
@ -5790,6 +5801,14 @@
"through2": "^2.0.3"
}
},
"ts-protoc-gen": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.12.0.tgz",
"integrity": "sha512-V7jnICJxKqalBrnJSMTW5tB9sGi48gOC325bfcM7TDNUItVOlaMM//rQmuo49ybipk/SyJTnWXgtJnhHCevNJw==",
"requires": {
"google-protobuf": "^3.6.1"
}
},
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",

Просмотреть файл

@ -362,18 +362,21 @@
"lint:fix": "npm run lint:fix:prettier && npm run lint:eslint -- --fix",
"lint:fix:prettier": "prettier --write '**/*.{json,svg,md,yml}'",
"compile:java": "cd java-gradle-tasks && ./gradlew generateLib",
"compile:jsprotos": "./compile-protos.sh",
"install:ext": "code --install-extension vscode-gradle-0.0.0.vsix --force",
"preinstall:ext": "gulp package",
"build": "gulp build"
},
"dependencies": {
"get-port": "^5.1.1",
"google-protobuf": "^3.11.4",
"strip-ansi": "^6.0.0",
"vscode-nls": "^4.1.1",
"ws": "^7.2.1"
},
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/google-protobuf": "^3.7.2",
"@types/mocha": "^7.0.1",
"@types/node": "^13.7.1",
"@types/sinon": "^7.5.1",
@ -395,6 +398,7 @@
"mocha": "^7.0.1",
"prettier": "^1.19.1",
"sinon": "^8.1.1",
"ts-protoc-gen": "^0.12.0",
"typescript": "^3.7.5",
"vsce": "^1.73.0",
"vscode-nls-dev": "^3.3.1",

Просмотреть файл

@ -0,0 +1,34 @@
syntax = "proto3";
package com.github.badsyntax.gradletasks.messages.client;
option java_package = "com.github.badsyntax.gradletasks.messages.client";
option java_multiple_files = false;
option java_outer_classname = "ClientMessage";
message GetTasks {
string source_dir = 2;
}
message RunTask {
string source_dir = 1;
string task = 2;
repeated string args = 3;
}
message StopTask {
string source_dir = 1;
string task = 2;
}
message StopGetTasks {
string source_dir = 2;
}
message Message {
oneof kind {
GetTasks get_tasks = 1;
RunTask run_task = 2;
StopTask stop_task = 3;
StopGetTasks stop_get_tasks = 4;
}
}

Просмотреть файл

@ -0,0 +1,66 @@
syntax = "proto3";
package com.github.badsyntax.gradletasks.messages.server;
option java_package = "com.github.badsyntax.gradletasks.messages.server";
option java_multiple_files = false;
option java_outer_classname = "ServerMessage";
message ActionCancelled {
string message = 1;
string source_dir = 2;
string task = 3;
}
message Progress {
string message = 1;
}
message RunTask {
string message = 1;
string task = 3;
}
message OutputChanged {
enum OutputType {
STDERR = 0;
STDOUT = 1;
}
string message = 1;
OutputType output_type = 3;
}
message Error {
string message = 1;
}
message Info {
string message = 1;
}
message Tasks {
string message = 1;
repeated GradleTask tasks = 2;
}
message GradleTask {
string name = 1;
string group = 2;
string path = 3;
string project = 4;
string buildFile = 5;
string rootProject = 6;
string description = 7;
}
message Message {
oneof kind {
ActionCancelled action_cancelled = 1;
Progress progress = 2;
RunTask run_task = 3;
OutputChanged output_changed = 4;
Error error = 5;
Info info = 6;
Tasks get_tasks = 7;
}
}

Просмотреть файл

@ -10,67 +10,11 @@ import { GradleTasksServer, ServerOptions } from './server';
import { logger } from './logger';
import { handleCancelledTaskMessage } from './tasks';
import * as ClientMessage from '../generated/proto/com/github/badsyntax/gradletasks/ClientMessage_pb';
import * as ServerMessage from '../generated/proto/com/github/badsyntax/gradletasks/ServerMessage_pb';
const localize = nls.loadMessageBundle();
enum ServerMessageType {
GRADLE_PROGRESS = 'GRADLE_PROGRESS',
GRADLE_OUTPUT = 'GRADLE_OUTPUT',
ERROR = 'ERROR',
ACTION_CANCELLED = 'ACTION_CANCELLED',
GENERIC_MESSAGE = 'GENERIC_MESSAGE'
}
export type GradleTask = {
path: string;
description: string;
name: string;
group: string;
project: string;
rootProject: string;
buildFile: string;
};
export interface ServerMessage {
type: string;
message?: string;
}
export interface GradleTasksServerMessage extends ServerMessage {
tasks: GradleTask[];
}
export interface ServerCancelledMessage extends ServerMessage {
task: string;
sourceDir: string;
}
export interface ServerOutputMessage extends ServerMessage {
outputType: 'STDOUT' | 'STDERR';
}
interface ClientMessage {
type: 'runTask' | 'getTasks' | 'stopTask' | 'stopGetTasks';
}
interface GetTasksMessage extends ClientMessage {
sourceDir: string;
}
interface RunTaskMessage extends ClientMessage {
sourceDir: string;
task: string;
args: string[];
}
interface StopTaskMessage extends ClientMessage {
sourceDir: string;
task: string;
}
interface StopGetTasksMessage extends ClientMessage {
sourceDir: string;
}
class WebSocketClient implements vscode.Disposable {
private autoReconnectInterval = 1 * 1000; // ms
private instance: WebSocket | undefined;
@ -128,7 +72,7 @@ class WebSocketClient implements vscode.Disposable {
});
}
send(data: string, callback: (err?: Error) => void): void {
send(data: Uint8Array, callback: (err?: Error) => void): void {
try {
this.instance!.send(data, callback);
} catch (err) {
@ -168,13 +112,6 @@ class WebSocketClient implements vscode.Disposable {
}
}
class Message {
constructor(private readonly message: ClientMessage) {}
toString(): string {
return JSON.stringify(this.message);
}
}
export class GradleTasksClient implements vscode.Disposable {
private wsClient: WebSocketClient | undefined;
@ -184,34 +121,36 @@ export class GradleTasksClient implements vscode.Disposable {
public readonly onConnect: vscode.Event<null> = this._onConnect.event;
private _onGradleProgress: vscode.EventEmitter<
ServerMessage
> = new vscode.EventEmitter<ServerMessage>();
public readonly onGradleProgress: vscode.Event<ServerMessage> = this
ServerMessage.Progress
> = new vscode.EventEmitter<ServerMessage.Progress>();
public readonly onGradleProgress: vscode.Event<ServerMessage.Progress> = this
._onGradleProgress.event;
private _onGradleOutput: vscode.EventEmitter<
ServerOutputMessage
> = new vscode.EventEmitter<ServerOutputMessage>();
public readonly onGradleOutput: vscode.Event<ServerOutputMessage> = this
._onGradleOutput.event;
ServerMessage.OutputChanged
> = new vscode.EventEmitter<ServerMessage.OutputChanged>();
public readonly onGradleOutput: vscode.Event<
ServerMessage.OutputChanged
> = this._onGradleOutput.event;
private _onGradleError: vscode.EventEmitter<
ServerMessage
> = new vscode.EventEmitter<ServerMessage>();
public readonly onGradleError: vscode.Event<ServerMessage> = this
ServerMessage.Error
> = new vscode.EventEmitter<ServerMessage.Error>();
public readonly onGradleError: vscode.Event<ServerMessage.Error> = this
._onGradleError.event;
private _onActionCancelled: vscode.EventEmitter<
ServerCancelledMessage
> = new vscode.EventEmitter<ServerCancelledMessage>();
public readonly onActionCancelled: vscode.Event<ServerCancelledMessage> = this
._onActionCancelled.event;
ServerMessage.ActionCancelled
> = new vscode.EventEmitter<ServerMessage.ActionCancelled>();
public readonly onActionCancelled: vscode.Event<
ServerMessage.ActionCancelled
> = this._onActionCancelled.event;
private _onMessage: vscode.EventEmitter<
ServerMessage
> = new vscode.EventEmitter<ServerMessage>();
public readonly onMessage: vscode.Event<ServerMessage> = this._onMessage
.event;
ServerMessage.Message
> = new vscode.EventEmitter<ServerMessage.Message>();
public readonly onMessage: vscode.Event<ServerMessage.Message> = this
._onMessage.event;
public constructor(
private readonly server: GradleTasksServer,
@ -256,7 +195,9 @@ export class GradleTasksClient implements vscode.Disposable {
this._onGradleProgress.dispose();
}
public async getTasks(sourceDir: string): Promise<GradleTask[] | undefined> {
public async getTasks(
sourceDir: string
): Promise<ServerMessage.GradleTask[] | void> {
if (this.wsClient) {
this.statusBarItem.text = localize(
'client.refreshingTasks',
@ -265,15 +206,16 @@ export class GradleTasksClient implements vscode.Disposable {
);
this.statusBarItem.show();
try {
const clientMessage: GetTasksMessage = {
type: 'getTasks',
sourceDir
};
await this.sendMessage(new Message(clientMessage));
const serverMessage = (await this.waitForServerMessage(
'GRADLE_TASKS'
)) as GradleTasksServerMessage;
return serverMessage.tasks;
const getTasks = new ClientMessage.GetTasks();
getTasks.setSourceDir(sourceDir);
const message = new ClientMessage.Message();
message.setGetTasks(getTasks);
await this.sendMessage(message);
const serverMessage: ServerMessage.Message = (await this.waitForServerMessage(
ServerMessage.Message.KindCase.GET_TASKS
)) as ServerMessage.Message;
return serverMessage.getGetTasks()?.getTasksList();
} catch (e) {
logger.error(
localize(
@ -293,20 +235,24 @@ export class GradleTasksClient implements vscode.Disposable {
sourceDir: string,
task: string,
args: string[],
outputListener: (message: ServerMessage) => void
outputListener: (message: ServerMessage.OutputChanged) => void
): Promise<void> {
if (this.wsClient) {
const outputEvent = this.onGradleOutput(outputListener);
this.statusBarItem.show();
const clientMessage: RunTaskMessage = {
type: 'runTask',
sourceDir,
task,
args
};
try {
await this.sendMessage(new Message(clientMessage));
await this.waitForServerMessage('GRADLE_RUN_TASK');
const runTask = new ClientMessage.RunTask();
runTask.setSourceDir(sourceDir);
runTask.setTask(task);
runTask.setArgsList(args);
const message = new ClientMessage.Message();
message.setRunTask(runTask);
await this.sendMessage(message);
await this.waitForServerMessage(
ServerMessage.Message.KindCase.RUN_TASK
);
} catch (e) {
logger.error(
localize(
@ -325,13 +271,14 @@ export class GradleTasksClient implements vscode.Disposable {
public async stopTask(sourceDir: string, task: string): Promise<void> {
if (this.wsClient) {
const clientMessage: StopTaskMessage = {
type: 'stopTask',
sourceDir,
task
};
try {
await this.sendMessage(new Message(clientMessage));
const stopTask = new ClientMessage.StopTask();
stopTask.setSourceDir(sourceDir);
stopTask.setTask(task);
const message = new ClientMessage.Message();
message.setStopTask(stopTask);
await this.sendMessage(message);
} catch (e) {
logger.error(
localize(
@ -348,22 +295,22 @@ export class GradleTasksClient implements vscode.Disposable {
public async stopGetTasks(sourceDir = ''): Promise<void> {
if (this.wsClient) {
const clientMessage: StopGetTasksMessage = {
type: 'stopGetTasks',
sourceDir
};
try {
await this.sendMessage(new Message(clientMessage));
const stopGetTasks = new ClientMessage.StopGetTasks();
stopGetTasks.setSourceDir(sourceDir);
const message = new ClientMessage.Message();
message.setStopGetTasks(stopGetTasks);
await this.sendMessage(message);
} finally {
this.statusBarItem.hide();
}
}
}
private async sendMessage(message: Message): Promise<void> {
private async sendMessage(message: ClientMessage.Message): Promise<void> {
try {
return await new Promise((resolve, reject) => {
this.wsClient!.send(message.toString(), (err?: Error) => {
this.wsClient!.send(message.serializeBinary(), (err?: Error) => {
if (err) {
reject(err);
} else {
@ -396,9 +343,9 @@ export class GradleTasksClient implements vscode.Disposable {
}
private waitForServerMessage(
type: string
): Promise<ServerMessage | Error | unknown> {
return Promise.race<ServerMessage | Error | unknown>([
type: ServerMessage.Message.KindCase
): Promise<ServerMessage.Message | Error | unknown> {
return Promise.race<ServerMessage.Tasks | Error | unknown>([
new Promise((_, reject) => {
const event = this.server.onStop(() => {
reject(
@ -413,8 +360,8 @@ export class GradleTasksClient implements vscode.Disposable {
});
}),
new Promise(resolve => {
const event = this.onMessage((message: ServerMessage) => {
if (message.type === type) {
const event = this.onMessage((message: ServerMessage.Message) => {
if (message.getKindCase() === type) {
resolve(message);
event.dispose();
}
@ -423,59 +370,73 @@ export class GradleTasksClient implements vscode.Disposable {
]);
}
private handleProgressMessage = (message: ServerMessage): void => {
const messageStr = message.message?.trim();
private handleProgressMessage = (message: ServerMessage.Progress): void => {
const messageStr = message.getMessage().trim();
if (messageStr) {
this.statusBarItem.text = `$(sync~spin) Gradle: ${messageStr}`;
}
};
private handleOutputMessage = (message: ServerOutputMessage): void => {
const logMessage = stripAnsi(message.message!).trim();
private handleOutputMessage = (
message: ServerMessage.OutputChanged
): void => {
const logMessage = stripAnsi(message.getMessage()).trim();
if (logMessage) {
logger.info(logMessage);
}
};
private handleMessage = (data: WebSocket.Data): void => {
let serverMessage: ServerMessage;
try {
serverMessage = JSON.parse(data.toString());
if (getIsDebugEnabled()) {
logger.debug(data.toString());
}
} catch (e) {
logger.error(
localize(
'client.errorParsingMessageFromServer',
'Unable to parse message from server: {0}',
e.message
)
);
return;
}
this._onMessage.fire(serverMessage);
switch (serverMessage.type) {
case ServerMessageType.GRADLE_PROGRESS:
this._onGradleProgress.fire(serverMessage);
break;
case ServerMessageType.GRADLE_OUTPUT:
this._onGradleOutput.fire(serverMessage as ServerOutputMessage);
break;
case ServerMessageType.ERROR:
this._onGradleError.fire(serverMessage);
break;
case ServerMessageType.ACTION_CANCELLED:
this._onActionCancelled.fire(serverMessage as ServerCancelledMessage);
break;
case ServerMessageType.GENERIC_MESSAGE:
const message = serverMessage.message?.trim();
if (message) {
logger.info(message);
if (data instanceof Buffer) {
let serverMessage: ServerMessage.Message;
try {
serverMessage = ServerMessage.Message.deserializeBinary(data as Buffer);
if (getIsDebugEnabled()) {
logger.debug(JSON.stringify(serverMessage.toObject()));
}
break;
default:
break;
} catch (e) {
logger.error(
localize(
'client.errorParsingMessageFromServer',
'Unable to parse message from server: {0}',
e.message
)
);
return;
}
this._onMessage.fire(serverMessage);
const {
PROGRESS,
OUTPUT_CHANGED,
ERROR,
ACTION_CANCELLED,
INFO
} = ServerMessage.Message.KindCase;
switch (serverMessage.getKindCase()) {
case PROGRESS:
this._onGradleProgress.fire(serverMessage.getProgress());
break;
case OUTPUT_CHANGED:
this._onGradleOutput.fire(serverMessage.getOutputChanged());
break;
case ERROR:
this._onGradleError.fire(serverMessage.getError());
break;
case ACTION_CANCELLED:
this._onActionCancelled.fire(serverMessage.getActionCancelled());
break;
case INFO:
const message = serverMessage
.getInfo()
?.getMessage()
.trim();
if (message) {
logger.info(message);
}
break;
default:
break;
}
}
};
}
@ -490,7 +451,7 @@ export function registerClient(
client.onConnect(() => {
vscode.commands.executeCommand('gradle.refresh', false);
});
client.onActionCancelled((message: ServerCancelledMessage): void => {
client.onActionCancelled((message: ServerMessage.ActionCancelled): void => {
handleCancelledTaskMessage(message);
});
return client;

Просмотреть файл

@ -1,14 +1,9 @@
import * as vscode from 'vscode';
import * as path from 'path';
import * as nls from 'vscode-nls';
import * as ServerMessage from '../generated/proto/com/github/badsyntax/gradletasks/ServerMessage_pb';
import { getIsAutoDetectionEnabled, getIsDebugEnabled } from './config';
import {
GradleTasksClient,
GradleTask,
ServerMessage,
ServerCancelledMessage
} from './client';
import { GradleTasksClient } from './client';
import { logger } from './logger';
const localize = nls.loadMessageBundle();
@ -170,9 +165,9 @@ export class GradleTaskProvider implements vscode.TaskProvider {
workspaceFolder: vscode.WorkspaceFolder,
projectFolder: vscode.Uri
): Promise<vscode.Task[]> {
const gradleTasks: GradleTask[] | undefined = await this.getGradleTasks(
projectFolder
);
const gradleTasks:
| ServerMessage.GradleTask[]
| void = await this.getGradleTasks(projectFolder);
if (!gradleTasks || !gradleTasks.length) {
return emptyTasks;
}
@ -180,28 +175,28 @@ export class GradleTaskProvider implements vscode.TaskProvider {
this.createVSCodeTaskFromGradleTask(
gradleTask,
workspaceFolder,
gradleTask.rootProject,
vscode.Uri.file(gradleTask.buildFile),
gradleTask.getRootproject(),
vscode.Uri.file(gradleTask.getBuildfile()),
projectFolder
)
);
}
private createVSCodeTaskFromGradleTask(
gradleTask: GradleTask,
gradleTask: ServerMessage.GradleTask,
workspaceFolder: vscode.WorkspaceFolder,
rootProject: string,
buildFile: vscode.Uri,
projectFolder: vscode.Uri,
args = ''
): vscode.Task {
const script = gradleTask.path.replace(/^:/, '');
const script = gradleTask.getPath().replace(/^:/, '');
const definition: GradleTaskDefinition = {
type: 'gradle',
script,
description: gradleTask.description,
group: (gradleTask.group || 'other').toLowerCase(),
project: gradleTask.project,
description: gradleTask.getDescription(),
group: (gradleTask.getGroup() || 'other').toLowerCase(),
project: gradleTask.getProject(),
buildFile: buildFile.fsPath,
rootProject,
projectFolder: projectFolder.fsPath,
@ -219,7 +214,7 @@ export class GradleTaskProvider implements vscode.TaskProvider {
private async getGradleTasks(
projectFolder: vscode.Uri
): Promise<GradleTask[] | undefined> {
): Promise<ServerMessage.GradleTask[] | void> {
return this.client?.getTasks(projectFolder.fsPath);
}
}
@ -281,8 +276,8 @@ class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
this.sourceDir,
this.task.definition.script,
args,
(message: ServerMessage) => {
this.writeEmitter.fire(message.message?.toString() + '\r\n');
(message: ServerMessage.OutputChanged) => {
this.writeEmitter.fire(message.getMessage() + '\r\n');
}
);
} finally {
@ -377,7 +372,7 @@ export function buildGradleServerTask(
taskType,
new vscode.ShellExecution(cmd, args, { cwd })
);
task.isBackground = true;
// task.isBackground = true; // this hides errors on task start
task.source = taskType;
task.presentationOptions = {
reveal: vscode.TaskRevealKind.Never,
@ -390,11 +385,11 @@ export function buildGradleServerTask(
}
export function handleCancelledTaskMessage(
message: ServerCancelledMessage
message: ServerMessage.ActionCancelled
): void {
setStoppedTaskAsComplete(message.task, message.sourceDir);
setStoppedTaskAsComplete(message.getTask(), message.getSourceDir());
logger.info(
localize('tasks.taskCancelled', 'Task cancelled: {0}', message.message)
localize('tasks.taskCancelled', 'Task cancelled: {0}', message.getMessage())
);
vscode.commands.executeCommand('gradle.explorerRender');
}