From 4c23ff0d1aba4084279c9a67a9f01c0e76c265ca Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Thu, 21 May 2026 09:59:34 +0200 Subject: [PATCH 1/7] fix hide method call --- frontend/webEditor/src/serialize/loadJson.ts | 10 +++++----- frontend/webEditor/src/serialize/saveFile.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/webEditor/src/serialize/loadJson.ts b/frontend/webEditor/src/serialize/loadJson.ts index a4c9628f..38cdd74c 100644 --- a/frontend/webEditor/src/serialize/loadJson.ts +++ b/frontend/webEditor/src/serialize/loadJson.ts @@ -65,7 +65,7 @@ export abstract class LoadJsonCommand extends Command { this.file = await this.getFile(context).catch(() => undefined); if (!this.file) { - this.loadingIndicator.hide(); + this.loadingIndicator.hideIndicator(); this.actionDispatcher.dispatch(InitializeCanvasBoundsAction.create(this.oldRoot.canvasBounds)); return this.oldRoot; } @@ -108,13 +108,13 @@ export abstract class LoadJsonCommand extends Command { this.oldFileName = this.fileName.getName(); this.fileName.setName(this.file.fileName); - this.loadingIndicator.hide(); + this.loadingIndicator.hideIndicator(); return this.newRoot; } catch (error) { this.logger.error(this, "Error loading model", error); this.newRoot = this.oldRoot; this.actionDispatcher.dispatch(InitializeCanvasBoundsAction.create(this.oldRoot.canvasBounds)); - this.loadingIndicator.hide(); + this.loadingIndicator.hideIndicator(); return this.oldRoot; } } @@ -143,7 +143,7 @@ export abstract class LoadJsonCommand extends Command { this.fileName.setName(this.oldFileName ?? "diagram"); - this.loadingIndicator.hide(); + this.loadingIndicator.hideIndicator(); return this.oldRoot ?? context.modelFactory.createRoot(EMPTY_ROOT); } @@ -175,7 +175,7 @@ export abstract class LoadJsonCommand extends Command { this.fileName.setName(this.file?.fileName ?? "diagram"); - this.loadingIndicator.hide(); + this.loadingIndicator.hideIndicator(); return this.newRoot ?? this.oldRoot ?? context.modelFactory.createRoot(EMPTY_ROOT); } diff --git a/frontend/webEditor/src/serialize/saveFile.ts b/frontend/webEditor/src/serialize/saveFile.ts index 17a414f4..1de63d97 100644 --- a/frontend/webEditor/src/serialize/saveFile.ts +++ b/frontend/webEditor/src/serialize/saveFile.ts @@ -25,7 +25,7 @@ export abstract class SaveFileCommand extends SavedDiagramCreatorCommand { this.downloadFile(file); } - this.loadingIndicator.hide(); + this.loadingIndicator.hideIndicator(); return context.root; } undo(context: CommandExecutionContext): CommandReturn { From 786b0e94909a2ae0b7c612cc9ca4719878136897 Mon Sep 17 00:00:00 2001 From: Alexander Vogt Date: Thu, 21 May 2026 10:00:50 +0200 Subject: [PATCH 2/7] use css for delay --- .../src/loadingIndicator/loadingIndicator.css | 15 +++++++++++ .../src/loadingIndicator/loadingIndicator.ts | 27 ++++++------------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/frontend/webEditor/src/loadingIndicator/loadingIndicator.css b/frontend/webEditor/src/loadingIndicator/loadingIndicator.css index 068ef24c..3acbddf3 100644 --- a/frontend/webEditor/src/loadingIndicator/loadingIndicator.css +++ b/frontend/webEditor/src/loadingIndicator/loadingIndicator.css @@ -17,6 +17,21 @@ color: white; background-color: rgba(0, 0, 0, 0.8); + + opacity: 0; + display: none; +} + +#loading-indicator-wrapper.show { + display: flex; + animation: show 0s forwards; + animation-delay: 0.2s; +} + +@keyframes show { + to { + opacity: 1; + } } #turning-circle { diff --git a/frontend/webEditor/src/loadingIndicator/loadingIndicator.ts b/frontend/webEditor/src/loadingIndicator/loadingIndicator.ts index eb8d3b8d..a674b4b0 100644 --- a/frontend/webEditor/src/loadingIndicator/loadingIndicator.ts +++ b/frontend/webEditor/src/loadingIndicator/loadingIndicator.ts @@ -5,7 +5,7 @@ export class LoadingIndicator extends AbstractUIExtension { static readonly ID = "loading-indicator"; private loadingIndicatorWrapper: HTMLElement | undefined; private loadingIndicatorText: HTMLElement | undefined; - private waitTimeout?: number; + private static VISIBLE_CLASS = "show"; id(): string { return LoadingIndicator.ID; @@ -16,7 +16,6 @@ export class LoadingIndicator extends AbstractUIExtension { protected initializeContents(containerElement: HTMLElement): void { this.loadingIndicatorWrapper = document.createElement("div"); this.loadingIndicatorWrapper.id = "loading-indicator-wrapper"; - this.loadingIndicatorWrapper.style.display = "none"; const loadingIndicator = document.createElement("div"); loadingIndicator.id = "turning-circle"; @@ -30,28 +29,18 @@ export class LoadingIndicator extends AbstractUIExtension { } public showIndicator(text?: string) { - this.waitTimeout = setTimeout(() => { - if (!this.waitTimeout) { - return; - } - if (this.loadingIndicatorWrapper) { - this.loadingIndicatorWrapper.style.display = "flex"; - if (this.loadingIndicatorText) { - this.loadingIndicatorText.innerText = text || "Loading..."; - } - this.loadingIndicatorWrapper.focus(); - this.waitTimeout = undefined; + if (this.loadingIndicatorWrapper) { + this.loadingIndicatorWrapper.classList.add(LoadingIndicator.VISIBLE_CLASS); + if (this.loadingIndicatorText) { + this.loadingIndicatorText.innerText = text || "Loading..."; } - }, 200); + this.loadingIndicatorWrapper.focus(); + } } public hideIndicator() { - if (this.waitTimeout) { - clearTimeout(this.waitTimeout); - this.waitTimeout = undefined; - } if (this.loadingIndicatorWrapper) { - this.loadingIndicatorWrapper.style.display = "none"; + this.loadingIndicatorWrapper.classList.remove(LoadingIndicator.VISIBLE_CLASS); } } } From 03d18408aaf697789e975d43813d6dc24e3d777a Mon Sep 17 00:00:00 2001 From: Huell Date: Sat, 23 May 2026 15:26:36 +0200 Subject: [PATCH 3/7] Feat: Switch from WebSocket to RestAPI --- .../standalone/Application.java | 19 +- .../org/dataflowanalysis/standalone/Main.java | 8 +- .../standalone/analysis/Converter.java | 148 ------------- .../standalone/api/AnalyzeServlet.java | 18 ++ .../standalone/api/LoadDDServlet.java | 13 ++ .../standalone/api/LoadPCMServlet.java | 13 ++ .../standalone/api/SaveDDServlet.java | 14 ++ .../standalone/api/Servlet.java | 46 ++++ .../standalone/server/ApiServer.java | 26 +++ .../standalone/services/AnalyzeService.java | 39 ++++ .../standalone/services/LoadDDService.java | 74 +++++++ .../standalone/services/LoadPCMService.java | 69 ++++++ .../standalone/services/SaveDDService.java | 40 ++++ .../standalone/services/Util.java | 88 ++++++++ .../websocket/WebSocketServerHandler.java | 204 ------------------ .../websocket/WebSocketServerUtils.java | 52 ----- frontend/webEditor/.gitignore | 3 +- .../src/dfdApiClient/dfdApiClient.ts | 45 ++++ .../webEditor/src/dfdApiClient/di.config.ts | 6 + frontend/webEditor/src/index.ts | 4 +- frontend/webEditor/src/serialize/analyze.ts | 6 +- .../src/serialize/loadDfdAndDdFile.ts | 6 +- frontend/webEditor/src/serialize/loadJson.ts | 1 + .../src/serialize/loadPalladioFile.ts | 6 +- .../src/serialize/saveDfdAndDdFile.ts | 6 +- .../webEditor/src/startUpAgent/di.config.ts | 2 - .../src/startUpAgent/webSocketConnect.ts | 10 - frontend/webEditor/src/webSocket/di.config.ts | 6 - frontend/webEditor/src/webSocket/webSocket.ts | 96 --------- frontend/webEditor/vite.config.ts | 8 + 30 files changed, 526 insertions(+), 550 deletions(-) delete mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/analysis/Converter.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/AnalyzeServlet.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadDDServlet.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadPCMServlet.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/SaveDDServlet.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/Servlet.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/SaveDDService.java create mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java delete mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerHandler.java delete mode 100644 backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerUtils.java create mode 100644 frontend/webEditor/src/dfdApiClient/dfdApiClient.ts create mode 100644 frontend/webEditor/src/dfdApiClient/di.config.ts delete mode 100644 frontend/webEditor/src/startUpAgent/webSocketConnect.ts delete mode 100644 frontend/webEditor/src/webSocket/di.config.ts delete mode 100644 frontend/webEditor/src/webSocket/webSocket.ts diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java index 8da6fb14..a7714245 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java @@ -2,7 +2,7 @@ import java.util.Scanner; -import org.dataflowanalysis.standalone.websocket.WebSocketServerUtils; +import org.dataflowanalysis.standalone.server.ApiServer; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; @@ -10,23 +10,18 @@ public class Application implements IApplication { @Override public Object start(IApplicationContext context) throws Exception { try { - Thread webSocketServer = WebSocketServerUtils.startWebSocketServer(); + ApiServer.start(); Scanner scanner = new Scanner(System.in); String input = ""; System.out.println("Type 'exit' to quit the program."); - - // Loop until user types "exit" - while(webSocketServer.isAlive()) { - while (!input.equalsIgnoreCase("exit")) { - input = scanner.nextLine(); - } - scanner.close(); - return IApplication.EXIT_OK; - }; - scanner.close(); + while (!input.equalsIgnoreCase("exit")) { + input = scanner.nextLine(); + } + scanner.close(); + return IApplication.EXIT_OK; } catch (Exception e) { e.printStackTrace(); } diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Main.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Main.java index 4e95de49..dde03394 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Main.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Main.java @@ -1,14 +1,12 @@ package org.dataflowanalysis.standalone; -import org.dataflowanalysis.standalone.websocket.WebSocketServerUtils; + +import org.dataflowanalysis.standalone.server.ApiServer; public class Main { public static void main(String[] args) { try { - Thread webSocketServer = WebSocketServerUtils.startWebSocketServer(); - - while(webSocketServer.isAlive()); - + ApiServer.start(); } catch (Exception e) { e.printStackTrace(); } diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/analysis/Converter.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/analysis/Converter.java deleted file mode 100644 index 534f5364..00000000 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/analysis/Converter.java +++ /dev/null @@ -1,148 +0,0 @@ -package org.dataflowanalysis.standalone.analysis; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.List; - -import org.apache.log4j.Logger; -import org.dataflowanalysis.analysis.dfd.dsl.DFDDSLContextProvider; -import org.dataflowanalysis.analysis.dfd.simple.DFDSimpleTransposeFlowGraphFinder; -import org.dataflowanalysis.analysis.dsl.AnalysisConstraint; -import org.dataflowanalysis.analysis.utils.StringView; -import org.dataflowanalysis.converter.dfd2web.DataFlowDiagramAndDictionary; -import org.dataflowanalysis.converter.dfd2web.DFD2WebConverter; -import org.dataflowanalysis.converter.pcm2dfd.PCM2DFDConverter; -import org.dataflowanalysis.converter.pcm2dfd.PCMConverterModel; -import org.dataflowanalysis.converter.web2dfd.Web2DFDConverter; -import org.dataflowanalysis.converter.web2dfd.WebEditorConverterModel; -import org.dataflowanalysis.converter.web2dfd.model.WebEditorDfd; -import org.dataflowanalysis.dfd.datadictionary.DataDictionary; -import org.dataflowanalysis.dfd.datadictionary.datadictionaryPackage; -import org.dataflowanalysis.dfd.dataflowdiagram.DataFlowDiagram; -import org.dataflowanalysis.dfd.dataflowdiagram.dataflowdiagramPackage; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.resource.Resource; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; - -public class Converter { - - private static final Logger logger = Logger.getLogger(Converter.class); - - /** - * Convertes a DFD from the Ecore to the WebEditor Json representation - * @param dfd File where DFD is saved - * @param dd File where DD is saved - * @return Created WebEditor Json representation - */ - public static WebEditorDfd convertDFD(File dfd, File dd){ - var converter = new DFD2WebConverter(); - - ResourceSet rs = new ResourceSetImpl(); - rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl()); - rs.getPackageRegistry().put(dataflowdiagramPackage.eNS_URI, dataflowdiagramPackage.eINSTANCE); - rs.getPackageRegistry().put(datadictionaryPackage.eNS_URI, datadictionaryPackage.eINSTANCE); - - Resource ddResource = rs.getResource(URI.createFileURI(dd.toString()), true); - Resource dfdResource = rs.getResource(URI.createFileURI(dfd.toString()), true); - EcoreUtil.resolveAll(rs); - EcoreUtil.resolveAll(ddResource); - EcoreUtil.resolveAll(dfdResource); - DataFlowDiagramAndDictionary dfdAndDD = new DataFlowDiagramAndDictionary((DataFlowDiagram)dfdResource.getContents().get(0), (DataDictionary)ddResource.getContents().get(0)); - - var newJson = converter.convert(dfdAndDD); - - return newJson.getModel(); - } - - - /** - * Convertes a Model in PCM representation into a WebEditor Json represenation - * @param usageModelFile File where Usage Model is saved - * @param allocationModelFile File where Allocation Model is saved - * @param nodeCharacteristicsFile File where Node Characteristics Model is saved - * @return Created WebEditor Json representation - */ - public static WebEditorDfd convertPCM(File usageModelFile, File allocationModelFile, File nodeCharacteristicsFile){ - var converter = new PCM2DFDConverter(); - var dfd = converter.convert(new PCMConverterModel(usageModelFile.toString(), allocationModelFile.toString(), nodeCharacteristicsFile.toString())); - - - var dfdConverter = new DFD2WebConverter(); - dfdConverter.setTransposeFlowGraphFinder(DFDSimpleTransposeFlowGraphFinder.class); - return dfdConverter.convert(dfd).getModel(); - } - - /** - * Analyzes a Model in WebEditor Json Representation and returns the analyzed Model - * @param webEditorDfd Model to be analyzed - * @return Analyzed Model - */ - public static WebEditorDfd analyzeAnnotate(WebEditorDfd webEditorDfd) { - var webEditorconverter = new Web2DFDConverter(); - var dd = webEditorconverter.convert(new WebEditorConverterModel(webEditorDfd)); - var dfdConverter = new DFD2WebConverter(); - if (webEditorDfd.constraints() != null && !webEditorDfd.constraints().isEmpty()) { - var constraints = parseConstraints(webEditorDfd); - dfdConverter.setConstraints(constraints); - } - var newJson = dfdConverter.convert(dd).getModel(); - - for (var child : newJson.model().children()) { - if (child.type().startsWith("node") && child.annotations() != null) { - var oldNode = webEditorDfd.model().children().stream().filter(node -> node.id().equals(child.id())).findAny().orElseThrow(); - //Necessary if ugly if we want to preserver custom annotations - var annotationsToRemove = oldNode.annotations().stream().filter(a -> a.message().startsWith("Propagated") || a.message().startsWith("Incoming") || a.message().startsWith("Constraint")).toList(); - oldNode.annotations().removeAll(annotationsToRemove); - oldNode.annotations().addAll(child.annotations()); - } - } - return webEditorDfd; - } - - /** - * Converts a model in WebEditor Json representation into the DFD metamodel representation and return the DFD files as a concatenated string - * @param webEditorDfd model in WebEditor Json representation to be converted - * @param name Name of the files to be created - * @return Concatenation of DFD and DD files as string - */ - public static String convertToDFDandStringify(WebEditorDfd webEditorDfd, String name) { - try { - var converter = new Web2DFDConverter(); - var dfd = converter.convert(new WebEditorConverterModel(webEditorDfd)); - String tempDir = System.getProperty("java.io.tmpdir"); - var dfdFile = new File(tempDir, name + ".dataflowdiagram"); - var ddFile = new File(tempDir, name + ".datadictionary"); - dfd.save(dfdFile.getParent(), name); - - String dfdContent = Files.readString(dfdFile.toPath()); - String ddContent = Files.readString(ddFile.toPath()); - - dfdFile.delete(); - ddFile.delete(); - return dfdContent + "\n" + ddContent; - - } catch (IOException e) { - e.printStackTrace(); - return "Error"; - } - } - - private static List parseConstraints(WebEditorDfd webEditorDfd) { - return webEditorDfd.constraints().stream() - .filter(it -> it.constraint() != null && !it.constraint().isEmpty()) - .map(it -> { - StringView string = new StringView("- " + it.name() + ": " + it.constraint().replace("\n", "")); - var constraint = AnalysisConstraint.fromString(string, new DFDDSLContextProvider()); - if (constraint.failed()) { - logger.error(constraint.getError()); - throw new IllegalArgumentException("Unable to parse constraint: " + it.name()); - } - var constraint2 = constraint.getResult(); - return constraint2; - }).toList(); - } -} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/AnalyzeServlet.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/AnalyzeServlet.java new file mode 100644 index 00000000..12fc8e39 --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/AnalyzeServlet.java @@ -0,0 +1,18 @@ +package org.dataflowanalysis.standalone.api; + +import org.dataflowanalysis.standalone.services.AnalyzeService; + +import com.fasterxml.jackson.core.JsonProcessingException; + +public class AnalyzeServlet extends Servlet { + private static final long serialVersionUID = 1L; + private final AnalyzeService analysisService = new AnalyzeService(); + + protected String doSpecific(String message, String name){ + try { + return analysisService.analyzeAnnotate(message); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("Json unparsable"); + } + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadDDServlet.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadDDServlet.java new file mode 100644 index 00000000..4ad1b41c --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadDDServlet.java @@ -0,0 +1,13 @@ +package org.dataflowanalysis.standalone.api; + +import org.dataflowanalysis.standalone.services.LoadDDService; + +public class LoadDDServlet extends Servlet { + + private static final long serialVersionUID = 1L; + private final LoadDDService loadDDService = new LoadDDService(); + + protected String doSpecific(String message, String name){ + return loadDDService.safeLoadAndConvertDFDString(message, name); + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadPCMServlet.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadPCMServlet.java new file mode 100644 index 00000000..1e895680 --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/LoadPCMServlet.java @@ -0,0 +1,13 @@ +package org.dataflowanalysis.standalone.api; + +import org.dataflowanalysis.standalone.services.LoadPCMService; + +public class LoadPCMServlet extends Servlet { + + private static final long serialVersionUID = 1L; + private final LoadPCMService loadPCMService = new LoadPCMService(); + + protected String doSpecific(String message, String name){ + return loadPCMService.safeLoadAndConvertPCMString(message); + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/SaveDDServlet.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/SaveDDServlet.java new file mode 100644 index 00000000..cdb110f0 --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/SaveDDServlet.java @@ -0,0 +1,14 @@ +package org.dataflowanalysis.standalone.api; + +import org.dataflowanalysis.standalone.services.SaveDDService; + + +public class SaveDDServlet extends Servlet { + + private static final long serialVersionUID = 1L; + private final SaveDDService saveDDService = new SaveDDService(); + + protected String doSpecific(String message, String name){ + return saveDDService.convertToDFDandStringify(message, name); + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/Servlet.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/Servlet.java new file mode 100644 index 00000000..64ec356d --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/api/Servlet.java @@ -0,0 +1,46 @@ +package org.dataflowanalysis.standalone.api; + +import java.io.IOException; +import java.util.stream.Collectors; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public abstract class Servlet extends HttpServlet{ + private static final long serialVersionUID = 1L; + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { + String message = request.getReader() + .lines() + .collect(Collectors.joining(System.lineSeparator())); + + String name = message.split(":")[0]; + message = message.replaceFirst(name + ":", ""); + + try { + var result = doSpecific(message, name); + writeText(response, HttpServletResponse.SC_OK, name + ":" + result); + } catch (IllegalArgumentException e) { + writeText(response, HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); + + } catch (Exception e) { + writeText(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal server error"); + } + } + + private void writeText(HttpServletResponse response, int status, String message) throws IOException { + response.setStatus(status); + response.setContentType("text/plain;charset=UTF-8"); + response.getWriter().write(message); + } + + /** + * Servlet specific activities + * @param message Incoming message content + * @param name Name of the diagram + * @return result + */ + protected abstract String doSpecific(String message, String name); +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java new file mode 100644 index 00000000..f828e440 --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java @@ -0,0 +1,26 @@ +package org.dataflowanalysis.standalone.server; + +import org.dataflowanalysis.standalone.api.AnalyzeServlet; +import org.dataflowanalysis.standalone.api.LoadDDServlet; +import org.dataflowanalysis.standalone.api.LoadPCMServlet; +import org.dataflowanalysis.standalone.api.SaveDDServlet; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; + +public class ApiServer { + public static void start() throws Exception { + Server server = new Server(8080); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); + context.setContextPath("/"); + + context.addServlet(AnalyzeServlet.class, "/api/analyze"); + context.addServlet(LoadDDServlet.class, "/api/loadDD"); + context.addServlet(LoadPCMServlet.class, "/api/loadPCM"); + context.addServlet(SaveDDServlet.class, "/api/saveDD"); + + server.setHandler(context); + server.start(); + server.join(); + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java new file mode 100644 index 00000000..6d5d182c --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java @@ -0,0 +1,39 @@ +package org.dataflowanalysis.standalone.services; + +import org.dataflowanalysis.converter.dfd2web.DFD2WebConverter; +import org.dataflowanalysis.converter.web2dfd.Web2DFDConverter; +import org.dataflowanalysis.converter.web2dfd.WebEditorConverterModel; + +import com.fasterxml.jackson.core.JsonProcessingException; + +public class AnalyzeService { + /** + * Analyzes a Model in WebEditor Json Representation and returns the analyzed Model + * @param webEditorDfd Model to be analyzed + * @return Analyzed Model + * @throws JsonProcessingException + */ + public String analyzeAnnotate(String diagramMessage) throws JsonProcessingException { + var webEditorDfd = Util.deserializeJson(diagramMessage); + + var webEditorConverter = new Web2DFDConverter(); + var dd = webEditorConverter.convert(new WebEditorConverterModel(webEditorDfd)); + var dfdConverter = new DFD2WebConverter(); + if (webEditorDfd.constraints() != null && !webEditorDfd.constraints().isEmpty()) { + var constraints = Util.parseConstraints(webEditorDfd); + dfdConverter.setConstraints(constraints); + } + var newJson = dfdConverter.convert(dd).getModel(); + + for (var child : newJson.model().children()) { + if (child.type().startsWith("node") && child.annotations() != null) { + var oldNode = webEditorDfd.model().children().stream().filter(node -> node.id().equals(child.id())).findAny().orElseThrow(); + //Necessary if ugly if we want to preserver custom annotations + var annotationsToRemove = oldNode.annotations().stream().filter(a -> a.message().startsWith("Propagated") || a.message().startsWith("Incoming") || a.message().startsWith("Constraint")).toList(); + oldNode.annotations().removeAll(annotationsToRemove); + oldNode.annotations().addAll(child.annotations()); + } + } + return Util.serializeJson(webEditorDfd); + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java new file mode 100644 index 00000000..efefc85a --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java @@ -0,0 +1,74 @@ +package org.dataflowanalysis.standalone.services; + +import java.io.File; + +import org.dataflowanalysis.converter.dfd2web.DFD2WebConverter; +import org.dataflowanalysis.converter.dfd2web.DataFlowDiagramAndDictionary; +import org.dataflowanalysis.converter.web2dfd.model.WebEditorDfd; +import org.dataflowanalysis.dfd.datadictionary.DataDictionary; +import org.dataflowanalysis.dfd.datadictionary.datadictionaryPackage; +import org.dataflowanalysis.dfd.dataflowdiagram.DataFlowDiagram; +import org.dataflowanalysis.dfd.dataflowdiagram.dataflowdiagramPackage; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; + +public class LoadDDService { + + /** + * Takes the DFD and DD in serialized form, saves them temporarily, then loads and converts them into an WebDFD and serialzes it + * @param diagramMessage Serialized DFD and DD + * @param name Name of the DFD and DD + * @return Serialized WebJson + */ + public String safeLoadAndConvertDFDString(String diagramMessage, String name) { + System.out.println(diagramMessage); + String[] parts = diagramMessage.split("\\R:DD:\\R", 2); + + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid DFD message: missing ':DD:' separator"); + } + + String dfdMessage = parts[0]; + String ddMessage = parts[1]; + + try { + var dfd = Util.createAndWriteTempFile(name + ".dataflowdiagram", dfdMessage); + var dd = Util.createAndWriteTempFile(name + ".datadictionary", ddMessage); + return Util.serializeJson(convertDFD(dfd, dd)); + } catch (Exception e) { + e.printStackTrace(); + throw new IllegalArgumentException("Invalid DFD Model"); + } + } + + /** + * Convertes a DFD from the Ecore to the WebEditor Json representation + * @param dfd File where DFD is saved + * @param dd File where DD is saved + * @return Created WebEditor Json representation + */ + private WebEditorDfd convertDFD(File dfd, File dd){ + var converter = new DFD2WebConverter(); + ResourceSet rs = new ResourceSetImpl(); + rs.getResourceFactoryRegistry().getExtensionToFactoryMap().put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl()); + rs.getPackageRegistry().put(dataflowdiagramPackage.eNS_URI, dataflowdiagramPackage.eINSTANCE); + rs.getPackageRegistry().put(datadictionaryPackage.eNS_URI, datadictionaryPackage.eINSTANCE); + + Resource ddResource = rs.getResource(URI.createFileURI(dd.toString()), true); + Resource dfdResource = rs.getResource(URI.createFileURI(dfd.toString()), true); + EcoreUtil.resolveAll(rs); + EcoreUtil.resolveAll(ddResource); + EcoreUtil.resolveAll(dfdResource); + DataFlowDiagramAndDictionary dfdAndDD = new DataFlowDiagramAndDictionary((DataFlowDiagram)dfdResource.getContents().get(0), (DataDictionary)ddResource.getContents().get(0)); + + var newJson = converter.convert(dfdAndDD); + + return newJson.getModel(); + } + + +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java new file mode 100644 index 00000000..601a103d --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java @@ -0,0 +1,69 @@ +package org.dataflowanalysis.standalone.services; + +import java.io.File; +import java.io.IOException; + +import org.dataflowanalysis.analysis.dfd.simple.DFDSimpleTransposeFlowGraphFinder; +import org.dataflowanalysis.converter.dfd2web.DFD2WebConverter; +import org.dataflowanalysis.converter.pcm2dfd.PCM2DFDConverter; +import org.dataflowanalysis.converter.pcm2dfd.PCMConverterModel; +import org.dataflowanalysis.converter.web2dfd.model.WebEditorDfd; + +public class LoadPCMService { + /** + * Takes a PCM model in serialized form, saves, loads and converts it into a WebDFD and serializes it + * @param message + * @return + */ + public String safeLoadAndConvertPCMString(String message) { + try { + String[] files = message.split("---FILE---"); + + File usageModelFile = null; + File allocationFile = null; + File nodeCharacteristicsFile = null; + + for (String fileSection : files) { + fileSection = fileSection.trim(); + if (fileSection.isEmpty()) continue; + + int firstColon = fileSection.indexOf(":"); + if (firstColon == -1) continue; + + String filename = fileSection.substring(0, firstColon); + String fileContent = fileSection.substring(firstColon + 1); + + File file = Util.createAndWriteTempFile(filename, fileContent); + + if (filename.endsWith(".usagemodel")) { + usageModelFile = file; + } else if (filename.endsWith(".allocation")) { + allocationFile = file; + } else if (filename.endsWith(".nodecharacteristics")) { + nodeCharacteristicsFile = file; + } + } + return Util.serializeJson(convertPCM(usageModelFile, allocationFile, nodeCharacteristicsFile)); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Invalid PCM Model"); + } + } + + /** + * Convertes a Model in PCM representation into a WebEditor Json represenation + * @param usageModelFile File where Usage Model is saved + * @param allocationModelFile File where Allocation Model is saved + * @param nodeCharacteristicsFile File where Node Characteristics Model is saved + * @return Created WebEditor Json representation + */ + private WebEditorDfd convertPCM(File usageModelFile, File allocationModelFile, File nodeCharacteristicsFile){ + var converter = new PCM2DFDConverter(); + var dfd = converter.convert(new PCMConverterModel(usageModelFile.toString(), allocationModelFile.toString(), nodeCharacteristicsFile.toString())); + + + var dfdConverter = new DFD2WebConverter(); + dfdConverter.setTransposeFlowGraphFinder(DFDSimpleTransposeFlowGraphFinder.class); + return dfdConverter.convert(dfd).getModel(); + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/SaveDDService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/SaveDDService.java new file mode 100644 index 00000000..cb9815d9 --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/SaveDDService.java @@ -0,0 +1,40 @@ +package org.dataflowanalysis.standalone.services; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import org.dataflowanalysis.converter.web2dfd.Web2DFDConverter; +import org.dataflowanalysis.converter.web2dfd.WebEditorConverterModel; + +public class SaveDDService { + /** + * Converts a model in WebEditor Json representation into the DFD metamodel representation and return the DFD files as a concatenated string + * @param webEditorDfd model in WebEditor Json representation to be converted + * @param name Name of the files to be created + * @return Concatenation of DFD and DD files as string + */ + public String convertToDFDandStringify(String diagramMessage, String name) { + var webEditorDfd = Util.deserializeJson(diagramMessage); + + try { + var converter = new Web2DFDConverter(); + var dfd = converter.convert(new WebEditorConverterModel(webEditorDfd)); + String tempDir = System.getProperty("java.io.tmpdir"); + var dfdFile = new File(tempDir, name + ".dataflowdiagram"); + var ddFile = new File(tempDir, name + ".datadictionary"); + dfd.save(dfdFile.getParent(), name); + + String dfdContent = Files.readString(dfdFile.toPath()); + String ddContent = Files.readString(ddFile.toPath()); + + dfdFile.delete(); + ddFile.delete(); + return dfdContent + "\n" + ddContent; + + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Invalid Model"); + } + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java new file mode 100644 index 00000000..2000701a --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java @@ -0,0 +1,88 @@ +package org.dataflowanalysis.standalone.services; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import org.apache.log4j.Logger; +import org.dataflowanalysis.analysis.dfd.dsl.DFDDSLContextProvider; +import org.dataflowanalysis.analysis.dsl.AnalysisConstraint; +import org.dataflowanalysis.analysis.utils.StringView; +import org.dataflowanalysis.converter.web2dfd.model.WebEditorDfd; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +public class Util { + private static final Logger logger = Logger.getLogger(Util.class); + + /** + * Deserializes WebDFD + * @param json Serialized WebDFD + * @return Deserialized WebDFD + */ + public static WebEditorDfd deserializeJson(String json){ + var objectMapper = new ObjectMapper(); + WebEditorDfd webEditorDfd; + try { + webEditorDfd = objectMapper.readValue(json, WebEditorDfd.class); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Invalid Json Model"); + } + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + return webEditorDfd; + } + + /** + * Serializes a webDFD + * @param webEditorDfd + * @return Serialized WebDFD + * @throws JsonProcessingException Should not happen in theory + */ + public static String serializeJson(WebEditorDfd webEditorDfd) throws JsonProcessingException { + var objectMapper = new ObjectMapper(); + return objectMapper.writeValueAsString(webEditorDfd); + } + + /** + * Takes the constraints from a WebDFD and turns them into AnalysisConstraint + * @param webEditorDfd Full WebDFD + * @return Converted AnalysisConstraints + */ + public static List parseConstraints(WebEditorDfd webEditorDfd) { + return webEditorDfd.constraints().stream() + .filter(it -> it.constraint() != null && !it.constraint().isEmpty()) + .map(it -> { + StringView string = new StringView("- " + it.name() + ": " + it.constraint().replace("\n", "")); + var constraint = AnalysisConstraint.fromString(string, new DFDDSLContextProvider()); + if (constraint.failed()) { + logger.error(constraint.getError()); + throw new IllegalArgumentException("Unable to parse constraint: " + it.name()); + } + var constraint2 = constraint.getResult(); + return constraint2; + }).toList(); + } + + /** + * Saves the content as a temporary file for further processing + * @param name Name of the temp file + * @param content Content of the temp file + * @return File Object + * @throws IOException + */ + public static File createAndWriteTempFile(String name, String content) throws IOException { + String tempDir = System.getProperty("java.io.tmpdir"); + var file = new File(tempDir, name); + file.deleteOnExit(); + FileWriter fileWriter = new FileWriter(file); + fileWriter.write(content); + fileWriter.close(); + return file; + } +} diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerHandler.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerHandler.java deleted file mode 100644 index 20e6b384..00000000 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerHandler.java +++ /dev/null @@ -1,204 +0,0 @@ -package org.dataflowanalysis.standalone.websocket; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; - -import org.apache.log4j.Level; -import org.apache.log4j.Logger; -import org.dataflowanalysis.converter.web2dfd.model.WebEditorDfd; -import org.dataflowanalysis.standalone.analysis.Converter; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.WebSocketAdapter; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; - -public class WebSocketServerHandler extends WebSocketAdapter -{ - private static Map sessions = new HashMap<>(); - private static int index = 0; - private final Logger logger = Logger.getLogger(WebSocketServerHandler.class); - - /** - * Assigns an ID for identification on websocket connection - * @param sess Session that was created and is saved for further communication - */ - @Override - public void onWebSocketConnect(Session sess) - { - super.onWebSocketConnect(sess); - logger.info("WS connection established"); - - sessions.put(index, sess); - try { - sess.getRemote().sendString("ID assigned:" + index); - } catch (IOException e) { - e.printStackTrace(); - } - index++; - } - - /** - * Handles incoming messages, if valid sends return message to identified session - * @param message Incoming message - */ - @Override - public void onWebSocketText(String message) - { - super.onWebSocketText(message); - var analysisThread = new Thread(() -> { - var id = Integer.parseInt(message.split(":")[0]); - String returnMessage = handleIncomingMessage(id, message.substring(message.indexOf(":")+1)); - - try { - if (!returnMessage.endsWith("null")) sessions.get(id).getRemote().sendString(returnMessage); - else {sessions.get(id).getRemote().sendString("Error: Unknown Error"); - } - } catch (IOException e) { - e.printStackTrace(); - } - - }); - analysisThread.start(); - } - - @Override - public void onWebSocketClose(int statusCode, String reason) - { - super.onWebSocketClose(statusCode, reason); - } - - @Override - public void onWebSocketError(Throwable cause) - { - super.onWebSocketError(cause); - cause.printStackTrace(System.err); - } - - private String handleIncomingMessage(int id, String message) { - logger.setLevel(Level.DEBUG); - logger.info("Message received"); - logger.debug(message); - - var objectMapper = new ObjectMapper(); - WebEditorDfd newJson = null; - - var name = message.split(":")[0]; - message = message.replaceFirst(name + ":", ""); - - try { - if (message.startsWith("Json:")) { - message = message.substring(message.indexOf(":") + 1); - newJson = deserializeJsonAndAnnotate(message); - } - else if (message.startsWith("Json2DFD:")) { - message = message.replaceFirst("Json2DFD:", ""); - var webEditorDfd = deserializeJson(message); - return name + ":" + Converter.convertToDFDandStringify(webEditorDfd, name); - } - else if (message.startsWith("DFD:")) { - newJson = safeLoadAndConvertDFDString(message, name); - } else { - newJson = safeLoadAndConvertPCMString(message); - } - } catch (IllegalArgumentException e) { - return "Error:" + e.getMessage(); - } - - try { - return name + ":" + objectMapper.writeValueAsString(newJson); - } catch (JsonProcessingException e) { - return "Error:" + " Unable to read Json"; - } - } - - private WebEditorDfd deserializeJsonAndAnnotate(String json){ - var objectMapper = new ObjectMapper(); - WebEditorDfd webEditorDfd; - try { - webEditorDfd = objectMapper.readValue(json, WebEditorDfd.class); - } catch (IOException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Invalid Json Model"); - } - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - return Converter.analyzeAnnotate(webEditorDfd); - } - - private WebEditorDfd deserializeJson(String json){ - var objectMapper = new ObjectMapper(); - WebEditorDfd webEditorDfd; - try { - webEditorDfd = objectMapper.readValue(json, WebEditorDfd.class); - } catch (IOException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Invalid Json Model"); - } - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - return webEditorDfd; - } - - private WebEditorDfd safeLoadAndConvertDFDString(String message, String name) { - message = message.replaceFirst("DFD:", ""); - var dfdMessage = message.split("\n:DD:\n")[0]; - var ddMessage = message.split("\n:DD:\n")[1]; - try { - var dfd = createAndWriteTempFile(name + ".dataflowdiagram", dfdMessage); - var dd = createAndWriteTempFile(name + ".datadictionary", ddMessage); - return Converter.convertDFD(dfd, dd); - } catch (IOException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Invalid DFD Model"); - } - } - - private WebEditorDfd safeLoadAndConvertPCMString(String message) { - try { - String[] files = message.split("---FILE---"); - - File usageModelFile = null; - File allocationFile = null; - File nodeCharacteristicsFile = null; - - for (String fileSection : files) { - fileSection = fileSection.trim(); - if (fileSection.isEmpty()) continue; - - int firstColon = fileSection.indexOf(":"); - if (firstColon == -1) continue; - - String filename = fileSection.substring(0, firstColon); - String fileContent = fileSection.substring(firstColon + 1); - - File file = createAndWriteTempFile(filename, fileContent); - - if (filename.endsWith(".usagemodel")) { - usageModelFile = file; - } else if (filename.endsWith(".allocation")) { - allocationFile = file; - } else if (filename.endsWith(".nodecharacteristics")) { - nodeCharacteristicsFile = file; - } - } - return Converter.convertPCM(usageModelFile, allocationFile, nodeCharacteristicsFile); - } catch (IOException e) { - e.printStackTrace(); - throw new IllegalArgumentException("Invalid PCM Model"); - } - } - - private File createAndWriteTempFile(String name, String content) throws IOException { - String tempDir = System.getProperty("java.io.tmpdir"); - var file = new File(tempDir, name); - file.deleteOnExit(); - FileWriter fileWriter = new FileWriter(file); - fileWriter.write(content); - fileWriter.close(); - return file; - } -} \ No newline at end of file diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerUtils.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerUtils.java deleted file mode 100644 index 80184684..00000000 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/websocket/WebSocketServerUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.dataflowanalysis.standalone.websocket; - -import java.time.Duration; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; - -public class WebSocketServerUtils { - public static Thread startWebSocketServer() { - Thread websocketServer = new Thread(() -> startServer()); - websocketServer.start(); - - return websocketServer; - } - - private static void startServer() { - try { - var server = new Server(); - var connector = new ServerConnector(server); - server.addConnector(connector); - - // Setup the basic application "context" for this application at "/" - // This is also known as the handler tree (in jetty speak) - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/"); - server.setHandler(context); - - - // Configure specific websocket behavior - JettyWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> - { - // Configure default max size - wsContainer.setMaxTextMessageSize(Long.MAX_VALUE); - wsContainer.setIdleTimeout(Duration.ofMinutes(60)); - - // Add websockets - wsContainer.addMapping("/events/*", WebSocketServerHandler.class); - }); - connector.setPort(3000); - server.start(); - server.join(); - - - } catch (Exception e) { - e.printStackTrace(); - } - } - - -} diff --git a/frontend/webEditor/.gitignore b/frontend/webEditor/.gitignore index 81071fd2..9fd32101 100644 --- a/frontend/webEditor/.gitignore +++ b/frontend/webEditor/.gitignore @@ -1,3 +1,4 @@ node_modules dist -src/helpUi/hash.json \ No newline at end of file +src/helpUi/hash.json +.vs/ \ No newline at end of file diff --git a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts new file mode 100644 index 00000000..28fa46cd --- /dev/null +++ b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts @@ -0,0 +1,45 @@ +import { inject, injectable } from "inversify"; +import { FileName } from "../fileName/fileName"; + +@injectable() +export class DfdApiClient { + + constructor( + @inject(FileName) private readonly fileName: FileName, + ) { } + + + public async requestDiagram(message: string, action: string) { + try { + const result = await this.sendMessage(message, action); + + const name = result.split(":")[0]; + const diagramMessage = result.replace(name + ":", ""); + + return { + fileName: name, + content: JSON.parse(diagramMessage), + }; + } catch (error) { + alert(error); + } + } + + public sendMessage(message: string, action: string): Promise { + return fetch("/api/" + action, { + method: "POST", + headers: { + "Content-Type": "text/plain;charset=UTF-8", + }, + body: this.fileName.getName() + ":" + message, + }).then(response => { + return response.text().then(responseText => { + if (!response.ok) { + throw new Error(responseText || `Request failed: ${response.status} ${response.statusText}`); + } + + return responseText; + }); + }); + } +} diff --git a/frontend/webEditor/src/dfdApiClient/di.config.ts b/frontend/webEditor/src/dfdApiClient/di.config.ts new file mode 100644 index 00000000..794f8c69 --- /dev/null +++ b/frontend/webEditor/src/dfdApiClient/di.config.ts @@ -0,0 +1,6 @@ +import { ContainerModule } from "inversify"; +import { DfdApiClient } from "./dfdApiClient"; + +export const dfdApiModule = new ContainerModule((bind) => { + bind(DfdApiClient).toSelf().inSingletonScope(); +}); diff --git a/frontend/webEditor/src/index.ts b/frontend/webEditor/src/index.ts index 17c67cc1..45e7f447 100644 --- a/frontend/webEditor/src/index.ts +++ b/frontend/webEditor/src/index.ts @@ -14,7 +14,7 @@ import { commonModule } from "./commonModule"; import { labelModule } from "./labels/di.config"; import { serializeModule } from "./serialize/di.config"; import { diagramModule } from "./diagram/di.config"; -import { webSocketModule } from "./webSocket/di.config"; +import { dfdApiModule } from "./dfdApiClient/di.config"; import { commandPaletteModule } from "./commandPalette/di.config"; import { layoutModule } from "./layout/di.config"; import { elkLayoutModule } from "sprotty-elk"; @@ -43,7 +43,7 @@ container.load( labelModule, diagramModule, serializeModule, - webSocketModule, + dfdApiModule, commandPaletteModule, elkLayoutModule, layoutModule, diff --git a/frontend/webEditor/src/serialize/analyze.ts b/frontend/webEditor/src/serialize/analyze.ts index 6b949abe..e3a4376b 100644 --- a/frontend/webEditor/src/serialize/analyze.ts +++ b/frontend/webEditor/src/serialize/analyze.ts @@ -4,7 +4,7 @@ import { CURRENT_VERSION, SavedDiagram } from "./SavedDiagram"; import { LabelTypeRegistry } from "../labels/LabelTypeRegistry"; import { SETTINGS } from "../settings/Settings"; import { FileName } from "../fileName/fileName"; -import { DfdWebSocket } from "../webSocket/webSocket"; +import { DfdApiClient } from "../dfdApiClient/dfdApiClient"; import { inject } from "inversify"; import { EditorModeController } from "../settings/editorMode"; import { Action } from "sprotty-protocol"; @@ -28,7 +28,7 @@ export class AnalyzeCommand extends LoadJsonCommand { @inject(ConstraintRegistry) constraintRegistry: ConstraintRegistry, @inject(SETTINGS.Mode) editorModeController: EditorModeController, @inject(FileName) fileName: FileName, - @inject(DfdWebSocket) private readonly dfdWebSocket: DfdWebSocket, + @inject(DfdApiClient) private readonly dfdApiClient: DfdApiClient, @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, ) { @@ -51,6 +51,6 @@ export class AnalyzeCommand extends LoadJsonCommand { mode: this.editorModeController.get(), version: CURRENT_VERSION, }; - return await this.dfdWebSocket.requestDiagram("Json:" + JSON.stringify(savedDiagram)); + return await this.dfdApiClient.requestDiagram(JSON.stringify(savedDiagram), "analyze"); } } diff --git a/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts b/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts index e1831bb2..e9f35521 100644 --- a/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts +++ b/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts @@ -2,7 +2,7 @@ import { Action } from "sprotty-protocol"; import { FileData, LoadJsonCommand } from "./loadJson"; import { chooseFiles } from "./fileChooser"; import { inject } from "inversify"; -import { DfdWebSocket } from "../webSocket/webSocket"; +import { DfdApiClient } from "../dfdApiClient/dfdApiClient"; import { TYPES, ILogger, ActionDispatcher } from "sprotty"; import { EditorModeController } from "../settings/editorMode"; import { LabelTypeRegistry } from "../labels/LabelTypeRegistry"; @@ -30,7 +30,7 @@ export class LoadDfdAndDdFileCommand extends LoadJsonCommand { @inject(ConstraintRegistry) constraintRegistry: ConstraintRegistry, @inject(SETTINGS.Mode) editorModeController: EditorModeController, @inject(FileName) fileName: FileName, - @inject(DfdWebSocket) private dfdWebSocket: DfdWebSocket, + @inject(DfdApiClient) private dfdWebSocket: DfdApiClient, @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, ) { @@ -57,7 +57,7 @@ export class LoadDfdAndDdFileCommand extends LoadJsonCommand { this.fileName.setName(files[0].fileName); return this.dfdWebSocket - .requestDiagram("DFD:" + dataflowFileContent + "\n:DD:\n" + dictionaryFileContent) + .requestDiagram(dataflowFileContent + "\n:DD:\n" + dictionaryFileContent, "loadDD") .catch((e) => { this.fileName.setName(oldFileName); throw e; diff --git a/frontend/webEditor/src/serialize/loadJson.ts b/frontend/webEditor/src/serialize/loadJson.ts index 38cdd74c..6387a2ca 100644 --- a/frontend/webEditor/src/serialize/loadJson.ts +++ b/frontend/webEditor/src/serialize/loadJson.ts @@ -112,6 +112,7 @@ export abstract class LoadJsonCommand extends Command { return this.newRoot; } catch (error) { this.logger.error(this, "Error loading model", error); + alert(error) this.newRoot = this.oldRoot; this.actionDispatcher.dispatch(InitializeCanvasBoundsAction.create(this.oldRoot.canvasBounds)); this.loadingIndicator.hideIndicator(); diff --git a/frontend/webEditor/src/serialize/loadPalladioFile.ts b/frontend/webEditor/src/serialize/loadPalladioFile.ts index d9576cc9..7bed276e 100644 --- a/frontend/webEditor/src/serialize/loadPalladioFile.ts +++ b/frontend/webEditor/src/serialize/loadPalladioFile.ts @@ -2,7 +2,7 @@ import { Action } from "sprotty-protocol"; import { FileData, LoadJsonCommand } from "./loadJson"; import { chooseFiles } from "./fileChooser"; import { inject } from "inversify"; -import { DfdWebSocket } from "../webSocket/webSocket"; +import { DfdApiClient } from "../dfdApiClient/dfdApiClient"; import { TYPES, ILogger, ActionDispatcher } from "sprotty"; import { EditorModeController } from "../settings/editorMode"; import { LabelTypeRegistry } from "../labels/LabelTypeRegistry"; @@ -40,7 +40,7 @@ export class LoadPalladioFileCommand extends LoadJsonCommand { @inject(SETTINGS.Mode) editorModeController: EditorModeController, @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, @inject(FileName) fileName: FileName, - @inject(DfdWebSocket) private dfdWebSocket: DfdWebSocket, + @inject(DfdApiClient) private dfdWebSocket: DfdApiClient, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, ) { super( @@ -71,7 +71,7 @@ export class LoadPalladioFileCommand extends LoadJsonCommand { this.fileName.setName(files[0].fileName); return this.dfdWebSocket - .requestDiagram(files.map((f) => `${f.fileName}:${f.content}`).join("---FILE---")) + .requestDiagram(files.map((f) => `${f.fileName}:${f.content}`).join("---FILE---"), "loadPCM") .catch((e) => { this.fileName.setName(oldFileName); throw e; diff --git a/frontend/webEditor/src/serialize/saveDfdAndDdFile.ts b/frontend/webEditor/src/serialize/saveDfdAndDdFile.ts index 728b9261..e88ca99c 100644 --- a/frontend/webEditor/src/serialize/saveDfdAndDdFile.ts +++ b/frontend/webEditor/src/serialize/saveDfdAndDdFile.ts @@ -4,7 +4,7 @@ import { SaveFileCommand } from "./saveFile"; import { inject } from "inversify"; import { LabelTypeRegistry } from "../labels/LabelTypeRegistry"; import { EditorModeController } from "../settings/editorMode"; -import { DfdWebSocket } from "../webSocket/webSocket"; +import { DfdApiClient } from "../dfdApiClient/dfdApiClient"; import { Action } from "sprotty-protocol"; import { SETTINGS } from "../settings/Settings"; import { ConstraintRegistry } from "../constraint/constraintRegistry"; @@ -26,7 +26,7 @@ export class SaveDfdAndDdFileCommand extends SaveFileCommand { @inject(LabelTypeRegistry) labelTypeRegistry: LabelTypeRegistry, @inject(ConstraintRegistry) constraintRegistry: ConstraintRegistry, @inject(SETTINGS.Mode) editorModeController: EditorModeController, - @inject(DfdWebSocket) private readonly dfdWebSocket: DfdWebSocket, + @inject(DfdApiClient) private readonly dfdApiClient: DfdApiClient, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, ) { super(labelTypeRegistry, constraintRegistry, editorModeController, loadingIndicator); @@ -35,7 +35,7 @@ export class SaveDfdAndDdFileCommand extends SaveFileCommand { async getFiles(context: CommandExecutionContext): Promise[]> { const savedDiagram = this.createSavedDiagram(context); - const response = await this.dfdWebSocket.sendMessage("Json2DFD:" + JSON.stringify(savedDiagram)); + const response = await this.dfdApiClient.sendMessage(JSON.stringify(savedDiagram), "saveDD"); const nameEndIndex = response.indexOf(":"); const name = response.substring(0, nameEndIndex); const endIndex = diff --git a/frontend/webEditor/src/startUpAgent/di.config.ts b/frontend/webEditor/src/startUpAgent/di.config.ts index 47281130..b71d630d 100644 --- a/frontend/webEditor/src/startUpAgent/di.config.ts +++ b/frontend/webEditor/src/startUpAgent/di.config.ts @@ -2,12 +2,10 @@ import { ContainerModule } from "inversify"; import { StartUpAgent } from "./StartUpAgent"; import { LoadDefaultUiExtensionsStartUpAgent } from "./LoadDefaultUiExtensions"; import { LoadDefaultDiagramStartUpAgent } from "./LoadDefaultDiagram"; -import { WebSocketConnectStartUpAgent } from "./webSocketConnect"; import { SettingsInitStartUpAgent } from "./settingsInit"; export const startUpAgentModule = new ContainerModule((bind) => { bind(StartUpAgent).to(LoadDefaultUiExtensionsStartUpAgent); bind(StartUpAgent).to(LoadDefaultDiagramStartUpAgent); - bind(StartUpAgent).to(WebSocketConnectStartUpAgent); bind(StartUpAgent).to(SettingsInitStartUpAgent); }); diff --git a/frontend/webEditor/src/startUpAgent/webSocketConnect.ts b/frontend/webEditor/src/startUpAgent/webSocketConnect.ts deleted file mode 100644 index 1e234f9b..00000000 --- a/frontend/webEditor/src/startUpAgent/webSocketConnect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IStartUpAgent } from "./StartUpAgent"; -import { inject } from "inversify"; -import { DfdWebSocket } from "../webSocket/webSocket"; - -export class WebSocketConnectStartUpAgent implements IStartUpAgent { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - constructor(@inject(DfdWebSocket) _: DfdWebSocket) {} - - run(): void {} -} diff --git a/frontend/webEditor/src/webSocket/di.config.ts b/frontend/webEditor/src/webSocket/di.config.ts deleted file mode 100644 index f1be5b84..00000000 --- a/frontend/webEditor/src/webSocket/di.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { ContainerModule } from "inversify"; -import { DfdWebSocket } from "./webSocket"; - -export const webSocketModule = new ContainerModule((bind) => { - bind(DfdWebSocket).toSelf().inSingletonScope(); -}); diff --git a/frontend/webEditor/src/webSocket/webSocket.ts b/frontend/webEditor/src/webSocket/webSocket.ts deleted file mode 100644 index 8811c528..00000000 --- a/frontend/webEditor/src/webSocket/webSocket.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { inject, injectable } from "inversify"; -import { ILogger, TYPES } from "sprotty"; -import { FileName } from "../fileName/fileName"; - -@injectable() -export class DfdWebSocket { - private webSocket?: WebSocket; - private webSocketId = -1; - private lastRequest: { - resolve?: (v: string) => void; - reject?: (e: Error) => void; - } = {}; - private static readonly WS_URL = "wss://websocket.dataflowanalysis.org/events/"; - - constructor( - @inject(TYPES.ILogger) private readonly logger: ILogger, - @inject(FileName) private readonly fileName: FileName, - ) { - this.init(); - } - - private init() { - this.webSocket = new WebSocket(DfdWebSocket.WS_URL); - - this.webSocket.onopen = () => { - this.logger.log(this, "WebSocket connection established."); - }; - - this.webSocket.onclose = () => { - this.logger.log(this, "WebSocket connection closed. Reconnecting..."); - this.reject(new Error("WebSocket connection closed")); - this.init(); - }; - this.webSocket.onerror = () => { - this.logger.log(this, "WebSocket error occurred."); - this.reject(new Error("WebSocket error occurred")); - this.init(); - }; - - this.webSocket.onmessage = (event) => { - const message = event.data as string; - this.logger.log(this, "WebSocket message received: " + message); - - if (message.startsWith("Error:")) { - this.reject(new Error(message)); - } - - if (message.startsWith("ID assigned:")) { - const parts = message.split(":"); - this.webSocketId = parseInt(parts[1].trim()); - this.logger.log(this, "WebSocket ID assigned: " + this.webSocketId); - return; - } - - if (this.lastRequest.resolve) { - this.lastRequest.resolve(message); - this.lastRequest.resolve = undefined; - this.lastRequest.reject = undefined; - } else { - this.logger.log(this, "No pending request to resolve."); - } - }; - } - - private reject(error: Error) { - if (this.lastRequest.reject) { - this.lastRequest.reject(error); - this.lastRequest.resolve = undefined; - this.lastRequest.reject = undefined; - } - } - - public async requestDiagram(message: string) { - const result = await this.sendMessage(message); - const name = result.split(":")[0]; - const diagramMessage = result.replace(name + ":", ""); - return { - fileName: name, - content: JSON.parse(diagramMessage), - }; - } - - public sendMessage(message: string): Promise { - const result = new Promise((resolve, reject) => { - this.lastRequest.resolve = resolve; - this.lastRequest.reject = reject; - }); - if (!this.webSocket || this.webSocket.readyState !== WebSocket.OPEN) { - this.reject(new Error("WebSocket is not connected")); - return result; - } - - this.webSocket.send(this.webSocketId + ":" + this.fileName.getName() + ":" + message); - return result; - } -} diff --git a/frontend/webEditor/vite.config.ts b/frontend/webEditor/vite.config.ts index f94e2fe6..0fea7024 100644 --- a/frontend/webEditor/vite.config.ts +++ b/frontend/webEditor/vite.config.ts @@ -20,4 +20,12 @@ export default defineConfig({ }, }, }, + server: { + proxy: { + "/api": { + target: "http://localhost:8080", + changeOrigin: true + } + } + } }); From 31fd6e558ef47aaa7b1198bffd7106b7125e4cfd Mon Sep 17 00:00:00 2001 From: Huell Date: Sat, 23 May 2026 15:34:57 +0200 Subject: [PATCH 4/7] Fix: set correct api adress --- .../webEditor/src/dfdApiClient/dfdApiClient.ts | 16 ++++++---------- frontend/webEditor/src/serialize/loadJson.ts | 2 +- frontend/webEditor/vite.config.ts | 10 +++++----- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts index 28fa46cd..5a747e11 100644 --- a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts +++ b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts @@ -3,13 +3,9 @@ import { FileName } from "../fileName/fileName"; @injectable() export class DfdApiClient { + constructor(@inject(FileName) private readonly fileName: FileName) {} - constructor( - @inject(FileName) private readonly fileName: FileName, - ) { } - - - public async requestDiagram(message: string, action: string) { + public async requestDiagram(message: string, action: string) { try { const result = await this.sendMessage(message, action); @@ -22,7 +18,7 @@ export class DfdApiClient { }; } catch (error) { alert(error); - } + } } public sendMessage(message: string, action: string): Promise { @@ -32,8 +28,8 @@ export class DfdApiClient { "Content-Type": "text/plain;charset=UTF-8", }, body: this.fileName.getName() + ":" + message, - }).then(response => { - return response.text().then(responseText => { + }).then((response) => { + return response.text().then((responseText) => { if (!response.ok) { throw new Error(responseText || `Request failed: ${response.status} ${response.statusText}`); } @@ -42,4 +38,4 @@ export class DfdApiClient { }); }); } -} +} diff --git a/frontend/webEditor/src/serialize/loadJson.ts b/frontend/webEditor/src/serialize/loadJson.ts index 6387a2ca..c3b69237 100644 --- a/frontend/webEditor/src/serialize/loadJson.ts +++ b/frontend/webEditor/src/serialize/loadJson.ts @@ -112,7 +112,7 @@ export abstract class LoadJsonCommand extends Command { return this.newRoot; } catch (error) { this.logger.error(this, "Error loading model", error); - alert(error) + alert(error); this.newRoot = this.oldRoot; this.actionDispatcher.dispatch(InitializeCanvasBoundsAction.create(this.oldRoot.canvasBounds)); this.loadingIndicator.hideIndicator(); diff --git a/frontend/webEditor/vite.config.ts b/frontend/webEditor/vite.config.ts index 0fea7024..eafe9746 100644 --- a/frontend/webEditor/vite.config.ts +++ b/frontend/webEditor/vite.config.ts @@ -23,9 +23,9 @@ export default defineConfig({ server: { proxy: { "/api": { - target: "http://localhost:8080", - changeOrigin: true - } - } - } + target: "https://websocket.dataflowanalysis.org/", + changeOrigin: true, + }, + }, + }, }); From f1615a2375c2d5df1fc300ab53be5eda5d0483f0 Mon Sep 17 00:00:00 2001 From: Huell Date: Sun, 24 May 2026 13:54:00 +0200 Subject: [PATCH 5/7] Feat: Get ready for production build --- .../org/dataflowanalysis/standalone/server/ApiServer.java | 4 +++- frontend/webEditor/src/dfdApiClient/dfdApiClient.ts | 4 +++- frontend/webEditor/src/serialize/loadJson.ts | 2 +- frontend/webEditor/vite.config.ts | 8 -------- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java index f828e440..f1bfcc52 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/server/ApiServer.java @@ -1,5 +1,7 @@ package org.dataflowanalysis.standalone.server; +import java.net.InetSocketAddress; + import org.dataflowanalysis.standalone.api.AnalyzeServlet; import org.dataflowanalysis.standalone.api.LoadDDServlet; import org.dataflowanalysis.standalone.api.LoadPCMServlet; @@ -9,7 +11,7 @@ public class ApiServer { public static void start() throws Exception { - Server server = new Server(8080); + Server server = new Server(new InetSocketAddress("localhost", 3000)); ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); context.setContextPath("/"); diff --git a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts index 5a747e11..cf99cb6a 100644 --- a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts +++ b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts @@ -22,7 +22,9 @@ export class DfdApiClient { } public sendMessage(message: string, action: string): Promise { - return fetch("/api/" + action, { + const API_BASE_URL = "https://webeditor.t-hueller.de"; + + return fetch(`${API_BASE_URL}/api/${action}`, { method: "POST", headers: { "Content-Type": "text/plain;charset=UTF-8", diff --git a/frontend/webEditor/src/serialize/loadJson.ts b/frontend/webEditor/src/serialize/loadJson.ts index c3b69237..f1b5b0a9 100644 --- a/frontend/webEditor/src/serialize/loadJson.ts +++ b/frontend/webEditor/src/serialize/loadJson.ts @@ -212,7 +212,7 @@ export abstract class LoadJsonCommand extends Command { return modelSchema; } - private async postLoadActions() { + public async postLoadActions() { if (!this.newRoot) { return; } diff --git a/frontend/webEditor/vite.config.ts b/frontend/webEditor/vite.config.ts index eafe9746..f94e2fe6 100644 --- a/frontend/webEditor/vite.config.ts +++ b/frontend/webEditor/vite.config.ts @@ -20,12 +20,4 @@ export default defineConfig({ }, }, }, - server: { - proxy: { - "/api": { - target: "https://websocket.dataflowanalysis.org/", - changeOrigin: true, - }, - }, - }, }); From 016eb0325d405ae670d88e0f6aafe1fb8fd0834f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20H=C3=BCller?= <62952208+01Parzival10@users.noreply.github.com> Date: Tue, 26 May 2026 12:26:38 +0200 Subject: [PATCH 6/7] Ensure file writer closure Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../src/org/dataflowanalysis/standalone/services/Util.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java index 2000701a..347c6a54 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java @@ -80,9 +80,9 @@ public static File createAndWriteTempFile(String name, String content) throws IO String tempDir = System.getProperty("java.io.tmpdir"); var file = new File(tempDir, name); file.deleteOnExit(); - FileWriter fileWriter = new FileWriter(file); - fileWriter.write(content); - fileWriter.close(); + try (FileWriter fileWriter = new FileWriter(file)) { + fileWriter.write(content); + } return file; } } From 930615ced7217574ba56a0985cf04478348ab887 Mon Sep 17 00:00:00 2001 From: Huell Date: Tue, 26 May 2026 13:00:26 +0200 Subject: [PATCH 7/7] Fix: Issues pointed out by copilot --- .../dataflowanalysis/standalone/Application.java | 12 ------------ .../standalone/services/AnalyzeService.java | 2 +- .../standalone/services/LoadDDService.java | 1 - .../standalone/services/LoadPCMService.java | 14 ++++++++------ .../dataflowanalysis/standalone/services/Util.java | 4 ++++ .../webEditor/src/dfdApiClient/dfdApiClient.ts | 5 +++-- .../webEditor/src/serialize/loadDfdAndDdFile.ts | 4 ++-- frontend/webEditor/src/serialize/loadJson.ts | 5 ++++- .../webEditor/src/serialize/loadPalladioFile.ts | 4 ++-- frontend/webEditor/vite.config.ts | 9 +++++++++ 10 files changed, 33 insertions(+), 27 deletions(-) diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java index a7714245..ebb81138 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/Application.java @@ -1,6 +1,5 @@ package org.dataflowanalysis.standalone; -import java.util.Scanner; import org.dataflowanalysis.standalone.server.ApiServer; import org.eclipse.equinox.app.IApplication; @@ -11,17 +10,6 @@ public class Application implements IApplication { public Object start(IApplicationContext context) throws Exception { try { ApiServer.start(); - - Scanner scanner = new Scanner(System.in); - String input = ""; - - System.out.println("Type 'exit' to quit the program."); - - while (!input.equalsIgnoreCase("exit")) { - input = scanner.nextLine(); - } - scanner.close(); - return IApplication.EXIT_OK; } catch (Exception e) { e.printStackTrace(); } diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java index 6d5d182c..6c8691b8 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/AnalyzeService.java @@ -28,7 +28,7 @@ public String analyzeAnnotate(String diagramMessage) throws JsonProcessingExcept for (var child : newJson.model().children()) { if (child.type().startsWith("node") && child.annotations() != null) { var oldNode = webEditorDfd.model().children().stream().filter(node -> node.id().equals(child.id())).findAny().orElseThrow(); - //Necessary if ugly if we want to preserver custom annotations + //Necessary if ugly if we want to preserve custom annotations var annotationsToRemove = oldNode.annotations().stream().filter(a -> a.message().startsWith("Propagated") || a.message().startsWith("Incoming") || a.message().startsWith("Constraint")).toList(); oldNode.annotations().removeAll(annotationsToRemove); oldNode.annotations().addAll(child.annotations()); diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java index efefc85a..ec4af7f2 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadDDService.java @@ -25,7 +25,6 @@ public class LoadDDService { * @return Serialized WebJson */ public String safeLoadAndConvertDFDString(String diagramMessage, String name) { - System.out.println(diagramMessage); String[] parts = diagramMessage.split("\\R:DD:\\R", 2); if (parts.length != 2) { diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java index 601a103d..a5995fa3 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/LoadPCMService.java @@ -59,11 +59,13 @@ public String safeLoadAndConvertPCMString(String message) { */ private WebEditorDfd convertPCM(File usageModelFile, File allocationModelFile, File nodeCharacteristicsFile){ var converter = new PCM2DFDConverter(); - var dfd = converter.convert(new PCMConverterModel(usageModelFile.toString(), allocationModelFile.toString(), nodeCharacteristicsFile.toString())); - - - var dfdConverter = new DFD2WebConverter(); - dfdConverter.setTransposeFlowGraphFinder(DFDSimpleTransposeFlowGraphFinder.class); - return dfdConverter.convert(dfd).getModel(); + try { + var dfd = converter.convert(new PCMConverterModel(usageModelFile.toString(), allocationModelFile.toString(), nodeCharacteristicsFile.toString())); + var dfdConverter = new DFD2WebConverter(); + dfdConverter.setTransposeFlowGraphFinder(DFDSimpleTransposeFlowGraphFinder.class); + return dfdConverter.convert(dfd).getModel(); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid PCM Model"); + } } } diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java index 2000701a..f0e8290c 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/src/org/dataflowanalysis/standalone/services/Util.java @@ -77,6 +77,10 @@ public static List parseConstraints(WebEditorDfd webEditorDf * @throws IOException */ public static File createAndWriteTempFile(String name, String content) throws IOException { + if (name.contains("..") || name.contains("/") || name.contains("\\")) { + throw new IllegalArgumentException("Invalid file name: " + name); + } + String tempDir = System.getProperty("java.io.tmpdir"); var file = new File(tempDir, name); file.deleteOnExit(); diff --git a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts index cf99cb6a..c5cdc7fc 100644 --- a/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts +++ b/frontend/webEditor/src/dfdApiClient/dfdApiClient.ts @@ -18,13 +18,14 @@ export class DfdApiClient { }; } catch (error) { alert(error); + throw (error); } } public sendMessage(message: string, action: string): Promise { - const API_BASE_URL = "https://webeditor.t-hueller.de"; + const apiUrl = `/api/${action}`; - return fetch(`${API_BASE_URL}/api/${action}`, { + return fetch(apiUrl, { method: "POST", headers: { "Content-Type": "text/plain;charset=UTF-8", diff --git a/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts b/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts index e9f35521..bf55ce04 100644 --- a/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts +++ b/frontend/webEditor/src/serialize/loadDfdAndDdFile.ts @@ -30,7 +30,7 @@ export class LoadDfdAndDdFileCommand extends LoadJsonCommand { @inject(ConstraintRegistry) constraintRegistry: ConstraintRegistry, @inject(SETTINGS.Mode) editorModeController: EditorModeController, @inject(FileName) fileName: FileName, - @inject(DfdApiClient) private dfdWebSocket: DfdApiClient, + @inject(DfdApiClient) private dfdApiClient: DfdApiClient, @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, ) { @@ -56,7 +56,7 @@ export class LoadDfdAndDdFileCommand extends LoadJsonCommand { const oldFileName = this.fileName.getName(); this.fileName.setName(files[0].fileName); - return this.dfdWebSocket + return this.dfdApiClient .requestDiagram(dataflowFileContent + "\n:DD:\n" + dictionaryFileContent, "loadDD") .catch((e) => { this.fileName.setName(oldFileName); diff --git a/frontend/webEditor/src/serialize/loadJson.ts b/frontend/webEditor/src/serialize/loadJson.ts index f1b5b0a9..ea687703 100644 --- a/frontend/webEditor/src/serialize/loadJson.ts +++ b/frontend/webEditor/src/serialize/loadJson.ts @@ -63,7 +63,10 @@ export abstract class LoadJsonCommand extends Command { this.loadingIndicator.showIndicator("Loading model..."); this.oldRoot = context.root; - this.file = await this.getFile(context).catch(() => undefined); + this.file = await this.getFile(context).catch((error) => { + alert(error); + return undefined; + }); if (!this.file) { this.loadingIndicator.hideIndicator(); this.actionDispatcher.dispatch(InitializeCanvasBoundsAction.create(this.oldRoot.canvasBounds)); diff --git a/frontend/webEditor/src/serialize/loadPalladioFile.ts b/frontend/webEditor/src/serialize/loadPalladioFile.ts index 7bed276e..388a5815 100644 --- a/frontend/webEditor/src/serialize/loadPalladioFile.ts +++ b/frontend/webEditor/src/serialize/loadPalladioFile.ts @@ -40,7 +40,7 @@ export class LoadPalladioFileCommand extends LoadJsonCommand { @inject(SETTINGS.Mode) editorModeController: EditorModeController, @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, @inject(FileName) fileName: FileName, - @inject(DfdApiClient) private dfdWebSocket: DfdApiClient, + @inject(DfdApiClient) private dfdApiClient: DfdApiClient, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, ) { super( @@ -70,7 +70,7 @@ export class LoadPalladioFileCommand extends LoadJsonCommand { const oldFileName = this.fileName.getName(); this.fileName.setName(files[0].fileName); - return this.dfdWebSocket + return this.dfdApiClient .requestDiagram(files.map((f) => `${f.fileName}:${f.content}`).join("---FILE---"), "loadPCM") .catch((e) => { this.fileName.setName(oldFileName); diff --git a/frontend/webEditor/vite.config.ts b/frontend/webEditor/vite.config.ts index f94e2fe6..0f9f0a86 100644 --- a/frontend/webEditor/vite.config.ts +++ b/frontend/webEditor/vite.config.ts @@ -20,4 +20,13 @@ export default defineConfig({ }, }, }, + server: { + proxy: { + "/api": { + target: "https://websocket.dataflowanalysis.org", + //target: "http://localhost:3000", + changeOrigin: true + } + } + } });