diff --git a/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerConfiguration.java b/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerConfiguration.java index 76f0538d4..d686468c7 100644 --- a/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerConfiguration.java +++ b/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerConfiguration.java @@ -1,16 +1,12 @@ package org.springframework.ide.vscode.commons.sprotty.scan; import org.eclipse.elk.alg.layered.options.LayeredMetaDataProvider; -import org.eclipse.elk.alg.layered.options.LayeredOptions; import org.eclipse.elk.core.data.LayoutMetaDataService; -import org.eclipse.sprotty.DefaultDiagramServer; import org.eclipse.sprotty.IDiagramExpansionListener; import org.eclipse.sprotty.IDiagramOpenListener; import org.eclipse.sprotty.IDiagramSelectionListener; -import org.eclipse.sprotty.IDiagramServer; import org.eclipse.sprotty.ILayoutEngine; import org.eclipse.sprotty.IModelUpdateListener; -import org.eclipse.sprotty.IPopupModelFactory; import org.eclipse.sprotty.SModelCloner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -19,12 +15,6 @@ import org.springframework.ide.vscode.commons.sprotty.elk.ElkLayoutEngine; @Configuration public class DiagramServerConfiguration { - @Bean - public IDiagramServer diagramServer() { - DefaultDiagramServer diagramServer = new DefaultDiagramServer("spring-boot"); - return diagramServer; - } - @Bean public IModelUpdateListener modelUpdateListener() { return new IModelUpdateListener.NullImpl(); } diff --git a/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerManager.java b/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerManager.java new file mode 100644 index 000000000..c1c0b94fe --- /dev/null +++ b/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramServerManager.java @@ -0,0 +1,14 @@ +package org.springframework.ide.vscode.commons.sprotty.scan; + +import java.util.function.Consumer; + +import org.eclipse.sprotty.ActionMessage; +import org.eclipse.sprotty.IDiagramServer; + +public interface DiagramServerManager /*extends IDiagramServer.Provider*/ { + + void setRemoteEndpoint(Consumer remoteEndpoint); + + void sendMessageToServer(ActionMessage message); + +} diff --git a/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramWebsocketServer.java b/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramWebsocketServer.java index 29435c58f..dfa95f601 100644 --- a/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramWebsocketServer.java +++ b/headless-services/commons/commons-sprotty/src/main/java/org/springframework/ide/vscode/commons/sprotty/scan/DiagramWebsocketServer.java @@ -4,7 +4,6 @@ import java.util.HashSet; import java.util.Set; import org.eclipse.sprotty.ActionMessage; -import org.eclipse.sprotty.IDiagramServer; import org.eclipse.sprotty.server.json.ActionTypeAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,8 +24,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; -import reactor.core.publisher.Mono; - @Controller public class DiagramWebsocketServer implements WebSocketConfigurer, InitializingBean { @@ -37,7 +34,7 @@ public class DiagramWebsocketServer implements WebSocketConfigurer, Initializing private Gson gson; @Autowired - private IDiagramServer diagramServer; + private DiagramServerManager diagramServers; @Autowired private SimpleLanguageServer server; @@ -55,10 +52,10 @@ public class DiagramWebsocketServer implements WebSocketConfigurer, Initializing initializeGson(); server.onSprottyMessage((jsonMessage) -> { ActionMessage actionMessage = gson.fromJson(jsonMessage, ActionMessage.class); - diagramServer.accept(actionMessage); + diagramServers.sendMessageToServer(actionMessage); }); server.doOnInitialized(() -> { - diagramServer.setRemoteEndpoint(message -> { + diagramServers.setRemoteEndpoint(message -> { sendMessage((JsonObject)gson.toJsonTree(message)); }); }); @@ -99,7 +96,7 @@ public class DiagramWebsocketServer implements WebSocketConfigurer, Initializing if (END_MESSAGE.equals(payload)) { ActionMessage actionMessage = gson.fromJson(buff.toString(), ActionMessage.class); buff = new StringBuilder(); - diagramServer.accept(actionMessage); + diagramServers.sendMessageToServer(actionMessage); } else { buff.append(payload); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/LiveBeanDiagramModel.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/LiveBeansDiagramServerManager.java similarity index 61% rename from headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/LiveBeanDiagramModel.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/LiveBeansDiagramServerManager.java index 73a0e69af..d9a77e566 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/LiveBeanDiagramModel.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/LiveBeansDiagramServerManager.java @@ -1,12 +1,16 @@ package org.springframework.ide.vscode.boot.app.diagram; -import java.time.Duration; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import org.eclipse.sprotty.ActionMessage; +import org.eclipse.sprotty.DefaultDiagramServer; import org.eclipse.sprotty.Dimension; import org.eclipse.sprotty.IDiagramServer; +import org.eclipse.sprotty.ILayoutEngine; import org.eclipse.sprotty.Point; import org.eclipse.sprotty.SCompartment; import org.eclipse.sprotty.SEdge; @@ -17,42 +21,78 @@ import org.eclipse.sprotty.SModelRoot; import org.eclipse.sprotty.SNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean; import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel; -import org.springframework.scheduling.TaskScheduler; +import org.springframework.ide.vscode.commons.sprotty.scan.DiagramServerManager; +import org.springframework.ide.vscode.commons.util.ExceptionUtil; import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; @Component -public class LiveBeanDiagramModel implements InitializingBean { - - private static final Logger log = LoggerFactory.getLogger(LiveBeanDiagramModel.class); - - @Autowired - private IDiagramServer diagramServer; +public class LiveBeansDiagramServerManager implements DiagramServerManager { + private static final Logger log = LoggerFactory.getLogger(LiveBeansDiagramServerManager.class); + + private Cache servers = CacheBuilder.newBuilder().build(); + @Autowired private RunningAppProvider runningAppProvider; @Autowired - @Qualifier("taskScheduler") - private TaskScheduler taskScheduler; - - - @Override - public void afterPropertiesSet() throws Exception { - taskScheduler.scheduleAtFixedRate(() -> diagramServer.setModel(generateModel()), Duration.ofSeconds(15)); + private ILayoutEngine layoutEngine; + + private Consumer remoteEndpoint; + +// @Override + private IDiagramServer getDiagramServer(String clientId) { + try { + return servers.get(clientId, () -> { + DefaultDiagramServer diagramServer = new DefaultDiagramServer(clientId); + diagramServer.setRemoteEndpoint(this::sendMessageToRemoteEndpoint); + diagramServer.setLayoutEngine(layoutEngine); + diagramServer.setModel(generateModel(clientId)); + return diagramServer; + }); + } catch (ExecutionException e) { + throw ExceptionUtil.unchecked(e); + } } - private SModelRoot generateModel() { + public void setRemoteEndpoint(Consumer remoteEndpoint) { + Assert.isNull(this.remoteEndpoint, "Can only be set once!"); + this.remoteEndpoint = remoteEndpoint; + } + + private void sendMessageToRemoteEndpoint(ActionMessage message) { + if (remoteEndpoint != null) { + remoteEndpoint.accept(message); + } + } + + public void sendMessageToServer(ActionMessage message) { + IDiagramServer server = getDiagramServer(message.getClientId()); + if (server != null) { + server.accept(message); + } + } + + private SModelRoot generateModel(String clientId) { try { Collection apps = runningAppProvider.getAllRunningSpringApps(); if (!apps.isEmpty()) { - return toSprottyGraph(apps.iterator().next()); + String indexStr = clientId.substring(clientId.lastIndexOf('-') + 1); + int index = Integer.valueOf(indexStr); + for (SpringBootApp springBootApp : apps) { + if (--index == 0) { + return toSprottyGraph(springBootApp); + } + } } } catch (Exception e) { log.error("{}", e); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/MockDiagramServerModel.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/MockDiagramServerModel.java index 0919e191f..4e44eebf0 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/MockDiagramServerModel.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/diagram/MockDiagramServerModel.java @@ -19,7 +19,7 @@ import org.springframework.stereotype.Component; import net.bytebuddy.utility.RandomString; -@Component +//@Component public class MockDiagramServerModel implements InitializingBean { @Autowired diff --git a/nodejs-packages/sprotty-live-beans-client/src/di.config.ts b/nodejs-packages/sprotty-live-beans-client/src/di.config.ts index 9f3262d0e..8298a8357 100644 --- a/nodejs-packages/sprotty-live-beans-client/src/di.config.ts +++ b/nodejs-packages/sprotty-live-beans-client/src/di.config.ts @@ -13,7 +13,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import {BeanNode, VSCodeWebViewDiagramServer} from "./model"; +import {BeanNode, ExampleWebsocketDiagramServer, VSCodeWebViewDiagramServer} from "./model"; console.log("Loading di.config"); import "reflect-metadata" @@ -47,14 +47,26 @@ import { } from "sprotty"; import {BeanNodeView, CircleNodeView, EdgeView, ExampleGraphView} from "./views"; -export default (useWebsocket: boolean) => { +export enum TransportMedium { + None, + Websocket, + LSP +} + +export default (transport: TransportMedium, clientId: string) => { require("sprotty/css/sprotty.css"); require("../css/diagram.css"); const circlegraphModule = new ContainerModule((bind, unbind, isBound, rebind) => { - if (useWebsocket) - bind(TYPES.ModelSource).to(VSCodeWebViewDiagramServer).inSingletonScope(); - else - bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope(); + switch (transport) { + case TransportMedium.Websocket: + bind(TYPES.ModelSource).to(ExampleWebsocketDiagramServer).inSingletonScope(); + break; + case TransportMedium.LSP: + bind(TYPES.ModelSource).to(VSCodeWebViewDiagramServer).inSingletonScope(); + break; + default: + bind(TYPES.ModelSource).to(LocalModelSource).inSingletonScope(); + } rebind(TYPES.ILogger).to(ConsoleLogger).inSingletonScope(); rebind(TYPES.LogLevel).toConstantValue(LogLevel.log); const context = { bind, unbind, isBound, rebind }; @@ -67,8 +79,9 @@ export default (useWebsocket: boolean) => { configureViewerOptions(context, { needsClientLayout: true, needsServerLayout: true, - baseDiv: 'spring-boot' + baseDiv: clientId, }); + console.log('di.config.ts : ' + clientId); }); const container = new Container();