Skip to content

Commit e1868db

Browse files
kaloyan-raevvparfonov
authored andcommitted
Rework Composer plugin to avoid using Everrest based Websocket calls (eclipse-che#5629)
* Rework Composer plugin to avoid using Everrest based Websocket calls Fixes eclipse-che#5348 Signed-off-by: Kaloyan Raev <kaloyan.r@zend.com> * Fix dependencies Signed-off-by: Vitalii Parfonov <vparfonov@redhat.com>
1 parent 6bc14af commit e1868db

14 files changed

Lines changed: 360 additions & 164 deletions

File tree

assembly/assembly-main/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
<appendAssemblyId>false</appendAssemblyId>
105105
<updateOnly>false</updateOnly>
106106
<descriptors>
107-
<descriptor>${project.basedir}/src/assembly/assembly.xml</descriptor>
107+
<descriptor>${project.basedir}/src/assembly/assembly.xml</descriptor>
108108
</descriptors>
109109
<finalName>eclipse-che-${project.version}</finalName>
110110
<tarLongFileMode>posix</tarLongFileMode>

plugins/plugin-composer/che-plugin-composer-ide/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
<groupId>javax.validation</groupId>
3939
<artifactId>validation-api</artifactId>
4040
</dependency>
41+
<dependency>
42+
<groupId>org.eclipse.che.core</groupId>
43+
<artifactId>che-core-api-core</artifactId>
44+
</dependency>
4145
<dependency>
4246
<groupId>org.eclipse.che.core</groupId>
4347
<artifactId>che-core-api-project-shared</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2016-2017 Rogue Wave Software, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Rogue Wave Software, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.eclipse.che.plugin.composer.ide;
12+
13+
import com.google.inject.Inject;
14+
import com.google.inject.Singleton;
15+
16+
import org.eclipse.che.api.core.jsonrpc.commons.RequestHandlerConfigurator;
17+
import org.eclipse.che.api.core.jsonrpc.commons.RequestTransmitter;
18+
import org.eclipse.che.plugin.composer.shared.dto.ComposerOutput;
19+
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
import java.util.function.Consumer;
23+
24+
import static org.eclipse.che.plugin.composer.shared.Constants.COMPOSER_CHANNEL_OUTPUT;
25+
import static org.eclipse.che.plugin.composer.shared.Constants.COMPOSER_CHANNEL_SUBSCRIBE;
26+
27+
/**
28+
* A mechanism for handling all messages from the Composer and applying
29+
* registered consumers.
30+
*
31+
* @author Kaloyan Raev
32+
*/
33+
@Singleton
34+
public class ComposerJsonRpcHandler {
35+
private static final String WS_AGENT_ENDPOINT = "ws-agent";
36+
37+
private RequestHandlerConfigurator configurator;
38+
39+
private Set<Consumer<ComposerOutput>> composerOutputConsumers = new HashSet<>();
40+
41+
private boolean isSubscribed = false;
42+
43+
@Inject
44+
public ComposerJsonRpcHandler(RequestHandlerConfigurator configurator) {
45+
this.configurator = configurator;
46+
47+
handleComposerMessages();
48+
}
49+
50+
@Inject
51+
private void subscribe(RequestTransmitter requestTransmitter) {
52+
if (isSubscribed) {
53+
return;
54+
}
55+
56+
requestTransmitter.newRequest()
57+
.endpointId(WS_AGENT_ENDPOINT)
58+
.methodName(COMPOSER_CHANNEL_SUBSCRIBE)
59+
.noParams()
60+
.sendAndSkipResult();
61+
62+
isSubscribed = true;
63+
}
64+
65+
/**
66+
* Adds consumer for the event with {@link ComposerOutput}.
67+
*
68+
* @param consumer
69+
* new consumer
70+
*/
71+
public void addComposerOutputHandler(Consumer<ComposerOutput> consumer) {
72+
composerOutputConsumers.add(consumer);
73+
}
74+
75+
private void handleComposerMessages() {
76+
configurator.newConfiguration()
77+
.methodName(COMPOSER_CHANNEL_OUTPUT)
78+
.paramsAsDto(ComposerOutput.class)
79+
.noResult()
80+
.withConsumer(archetypeOutput -> composerOutputConsumers.forEach(it -> it.accept(archetypeOutput)));
81+
}
82+
}

plugins/plugin-composer/che-plugin-composer-ide/src/main/java/org/eclipse/che/plugin/composer/ide/communication/ComposerOutputHandler.java

Lines changed: 26 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2016 Rogue Wave Software, Inc.
2+
* Copyright (c) 2016-2017 Rogue Wave Software, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -12,26 +12,14 @@
1212

1313
import com.google.inject.Inject;
1414
import com.google.inject.Singleton;
15-
import com.google.web.bindery.event.shared.EventBus;
1615

17-
import org.eclipse.che.api.promises.client.Operation;
18-
import org.eclipse.che.api.promises.client.OperationException;
1916
import org.eclipse.che.ide.api.app.AppContext;
20-
import org.eclipse.che.ide.api.machine.WsAgentStateController;
21-
import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent;
22-
import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler;
23-
import org.eclipse.che.ide.dto.DtoFactory;
2417
import org.eclipse.che.ide.console.CommandConsoleFactory;
2518
import org.eclipse.che.ide.console.DefaultOutputConsole;
2619
import org.eclipse.che.ide.processes.panel.ProcessesPanelPresenter;
27-
import org.eclipse.che.ide.util.loging.Log;
28-
import org.eclipse.che.ide.websocket.MessageBus;
29-
import org.eclipse.che.ide.websocket.WebSocketException;
30-
import org.eclipse.che.ide.websocket.events.MessageHandler;
20+
import org.eclipse.che.plugin.composer.ide.ComposerJsonRpcHandler;
3121
import org.eclipse.che.plugin.composer.shared.dto.ComposerOutput;
3222

33-
import static org.eclipse.che.plugin.composer.shared.Constants.COMPOSER_CHANNEL_NAME;
34-
3523
/**
3624
* Handler which receives messages from the Composer tool.
3725
*
@@ -40,77 +28,43 @@
4028
@Singleton
4129
public class ComposerOutputHandler {
4230

43-
private final EventBus eventBus;
44-
private final DtoFactory factory;
4531
private final ProcessesPanelPresenter processesPanelPresenter;
46-
private final CommandConsoleFactory commandConsoleFactory;
4732
private final AppContext appContext;
4833

34+
private DefaultOutputConsole outputConsole;
35+
4936
@Inject
50-
public ComposerOutputHandler(EventBus eventBus,
51-
DtoFactory factory,
52-
WsAgentStateController wsAgentStateController,
37+
public ComposerOutputHandler(ComposerJsonRpcHandler composerJsonRpcHandler,
5338
ProcessesPanelPresenter processesPanelPresenter,
5439
CommandConsoleFactory commandConsoleFactory,
5540
AppContext appContext) {
56-
this.eventBus = eventBus;
57-
this.factory = factory;
5841
this.processesPanelPresenter = processesPanelPresenter;
59-
this.commandConsoleFactory = commandConsoleFactory;
6042
this.appContext = appContext;
6143

62-
handleOperations(factory, wsAgentStateController);
63-
}
44+
composerJsonRpcHandler.addComposerOutputHandler(this::onComposerOutput);
6445

65-
private void handleOperations(final DtoFactory factory, final WsAgentStateController wsAgentStateController) {
66-
eventBus.addHandler(WsAgentStateEvent.TYPE, new WsAgentStateHandler() {
67-
@Override
68-
public void onWsAgentStarted(WsAgentStateEvent event) {
69-
wsAgentStateController.getMessageBus().then(new Operation<MessageBus>() {
70-
@Override
71-
public void apply(MessageBus messageBus) throws OperationException {
72-
handleComposerOutput(messageBus);
73-
}
74-
});
75-
}
76-
77-
@Override
78-
public void onWsAgentStopped(WsAgentStateEvent event) {
79-
}
80-
});
46+
outputConsole = (DefaultOutputConsole) commandConsoleFactory.create("Composer");
8147
}
8248

83-
private void handleComposerOutput(final MessageBus messageBus) {
84-
final DefaultOutputConsole outputConsole = (DefaultOutputConsole) commandConsoleFactory.create("Composer");
85-
86-
try {
87-
messageBus.subscribe(COMPOSER_CHANNEL_NAME, new MessageHandler() {
88-
@Override
89-
public void onMessage(String message) {
90-
Log.info(getClass(), message);
91-
ComposerOutput archetypeOutput = factory.createDtoFromJson(message, ComposerOutput.class);
92-
processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), outputConsole);
93-
switch (archetypeOutput.getState()) {
94-
case START:
95-
outputConsole.clearOutputsButtonClicked();
96-
outputConsole.printText(archetypeOutput.getOutput(),"green");
97-
break;
98-
case IN_PROGRESS:
99-
outputConsole.printText(archetypeOutput.getOutput());
100-
break;
101-
case DONE:
102-
outputConsole.printText(archetypeOutput.getOutput(),"green");
103-
break;
104-
case ERROR:
105-
outputConsole.printText(archetypeOutput.getOutput(),"red");
106-
break;
107-
default:
108-
break;
109-
}
110-
}
111-
});
112-
} catch (WebSocketException e) {
113-
e.printStackTrace();
49+
private void onComposerOutput(ComposerOutput output) {
50+
String message = output.getOutput();
51+
switch (output.getState()) {
52+
case START:
53+
processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), outputConsole);
54+
outputConsole.clearOutputsButtonClicked();
55+
outputConsole.printText(message, "green");
56+
break;
57+
case IN_PROGRESS:
58+
outputConsole.printText(message);
59+
break;
60+
case DONE:
61+
outputConsole.printText(message, "green");
62+
break;
63+
case ERROR:
64+
outputConsole.printText(message, "red");
65+
break;
66+
default:
67+
break;
11468
}
11569
}
11670

plugins/plugin-composer/che-plugin-composer-server/pom.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@
2525
<groupId>com.google.code.gson</groupId>
2626
<artifactId>gson</artifactId>
2727
</dependency>
28+
<dependency>
29+
<groupId>com.google.guava</groupId>
30+
<artifactId>guava</artifactId>
31+
</dependency>
2832
<dependency>
2933
<groupId>com.google.inject</groupId>
3034
<artifactId>guice</artifactId>
@@ -33,10 +37,22 @@
3337
<groupId>com.google.inject.extensions</groupId>
3438
<artifactId>guice-multibindings</artifactId>
3539
</dependency>
40+
<dependency>
41+
<groupId>javax.annotation</groupId>
42+
<artifactId>javax.annotation-api</artifactId>
43+
</dependency>
44+
<dependency>
45+
<groupId>javax.inject</groupId>
46+
<artifactId>javax.inject</artifactId>
47+
</dependency>
3648
<dependency>
3749
<groupId>org.eclipse.che.core</groupId>
3850
<artifactId>che-core-api-core</artifactId>
3951
</dependency>
52+
<dependency>
53+
<groupId>org.eclipse.che.core</groupId>
54+
<artifactId>che-core-api-dto</artifactId>
55+
</dependency>
4056
<dependency>
4157
<groupId>org.eclipse.che.core</groupId>
4258
<artifactId>che-core-api-project</artifactId>

plugins/plugin-composer/che-plugin-composer-server/src/main/java/org/eclipse/che/plugin/composer/server/ComposerModule.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2016 Rogue Wave Software, Inc.
2+
* Copyright (c) 2016-2017 Rogue Wave Software, Inc.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License v1.0
55
* which accompanies this distribution, and is available at
@@ -17,6 +17,7 @@
1717
import org.eclipse.che.api.project.server.type.ProjectTypeDef;
1818
import org.eclipse.che.api.project.server.type.ValueProviderFactory;
1919
import org.eclipse.che.inject.DynaModule;
20+
import org.eclipse.che.plugin.composer.server.executor.ComposerJsonRpcMessenger;
2021
import org.eclipse.che.plugin.composer.server.projecttype.ComposerProjectGenerator;
2122
import org.eclipse.che.plugin.composer.server.projecttype.ComposerProjectInitializer;
2223
import org.eclipse.che.plugin.composer.server.projecttype.ComposerProjectType;
@@ -45,5 +46,7 @@ protected void configure() {
4546
Multibinder<ProjectHandler> projectHandlerMultibinder = newSetBinder(binder(), ProjectHandler.class);
4647
projectHandlerMultibinder.addBinding().to(ComposerProjectGenerator.class);
4748
projectHandlerMultibinder.addBinding().to(ComposerProjectInitializer.class);
49+
50+
bind(ComposerJsonRpcMessenger.class);
4851
}
4952
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2016-2017 Rogue Wave Software, Inc.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Rogue Wave Software, Inc. - initial API and implementation
10+
*******************************************************************************/
11+
package org.eclipse.che.plugin.composer.server.executor;
12+
13+
import org.eclipse.che.api.core.notification.EventService;
14+
import org.eclipse.che.api.core.util.AbstractLineConsumer;
15+
import org.eclipse.che.api.core.util.LineConsumer;
16+
import org.eclipse.che.api.core.util.ProcessUtil;
17+
import org.eclipse.che.api.core.util.ValueHolder;
18+
import org.eclipse.che.api.core.util.Watchdog;
19+
import org.eclipse.che.plugin.composer.shared.dto.ComposerOutput;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
import java.io.File;
24+
import java.io.IOException;
25+
import java.util.Arrays;
26+
import java.util.concurrent.TimeUnit;
27+
import java.util.concurrent.TimeoutException;
28+
29+
import javax.inject.Inject;
30+
import javax.inject.Singleton;
31+
32+
/**
33+
* @author Kaloyan Raev
34+
*/
35+
@Singleton
36+
public class ComposerCommandExecutor {
37+
38+
private EventService eventService;
39+
40+
@Inject
41+
public ComposerCommandExecutor(EventService eventService) {
42+
this.eventService = eventService;
43+
}
44+
45+
private static final Logger LOG = LoggerFactory.getLogger(ComposerCommandExecutor.class);
46+
47+
public void execute(String[] commandLine, File workDir)
48+
throws TimeoutException, IOException, InterruptedException {
49+
ProcessBuilder pb = new ProcessBuilder(commandLine).redirectErrorStream(true).directory(workDir);
50+
51+
eventService.publish(new ComposerOutputImpl(String.join(" ", commandLine), ComposerOutput.State.START));
52+
53+
LineConsumer lineConsumer = new AbstractLineConsumer() {
54+
@Override
55+
public void writeLine(String line) throws IOException {
56+
eventService.publish(new ComposerOutputImpl(line, ComposerOutput.State.IN_PROGRESS));
57+
}
58+
};
59+
60+
// process will be stopped after timeout
61+
Watchdog watcher = new Watchdog(10, TimeUnit.MINUTES);
62+
63+
try {
64+
final Process process = pb.start();
65+
final ValueHolder<Boolean> isTimeoutExceeded = new ValueHolder<>(false);
66+
watcher.start(() -> {
67+
isTimeoutExceeded.set(true);
68+
ProcessUtil.kill(process);
69+
});
70+
// consume logs until process ends
71+
ProcessUtil.process(process, lineConsumer);
72+
process.waitFor();
73+
eventService.publish(new ComposerOutputImpl("Done", ComposerOutput.State.DONE));
74+
if (isTimeoutExceeded.get()) {
75+
LOG.error("Command time expired : command-line " + Arrays.toString(commandLine));
76+
eventService.publish(new ComposerOutputImpl("Installing dependencies time expired", ComposerOutput.State.ERROR));
77+
throw new TimeoutException();
78+
} else if (process.exitValue() != 0) {
79+
LOG.error("Command failed : command-line " + Arrays.toString(commandLine));
80+
eventService.publish(new ComposerOutputImpl("Error occurred", ComposerOutput.State.ERROR));
81+
throw new IOException("Process failed. Exit code " + process.exitValue() + " command-line : " + Arrays.toString(commandLine));
82+
}
83+
} finally {
84+
watcher.stop();
85+
}
86+
}
87+
88+
}

0 commit comments

Comments
 (0)