diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/.classpath b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/.classpath index cef1abac..998a9737 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/.classpath +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/.classpath @@ -1,11 +1,36 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/META-INF/MANIFEST.MF b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/META-INF/MANIFEST.MF index 7dc618d1..530a84b9 100644 --- a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/META-INF/MANIFEST.MF +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/META-INF/MANIFEST.MF @@ -2,9 +2,9 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: org.dataflowanalysis.standalone Bundle-SymbolicName: org.dataflowanalysis.standalone;singleton:=true -Bundle-Version: 1.0.0.qualifier -Require-Bundle: org.dataflowanalysis.dfd.datadictionary;bundle-version="2.0.0", - org.dataflowanalysis.dfd.dataflowdiagram;bundle-version="2.0.0", +Bundle-Version: 3.0.0.qualifier +Require-Bundle: org.dataflowanalysis.dfd.datadictionary, + org.dataflowanalysis.dfd.dataflowdiagram, org.eclipse.jetty.websocket.server;bundle-version="11.0.24", org.eclipse.jetty.servlet;bundle-version="11.0.24", org.eclipse.jetty.http;bundle-version="11.0.24", @@ -19,10 +19,10 @@ Require-Bundle: org.dataflowanalysis.dfd.datadictionary;bundle-version="2.0.0", org.eclipse.jetty.websocket.common;bundle-version="11.0.24", org.eclipse.jetty.websocket.servlet;bundle-version="11.0.24", org.slf4j.api;bundle-version="1.7.30", - org.dataflowanalysis.analysis;bundle-version="3.0.0", - org.dataflowanalysis.analysis.dfd;bundle-version="3.0.0", - org.dataflowanalysis.converter;bundle-version="3.0.0", - org.dataflowanalysis.pcm.extension.nodecharacteristics;bundle-version="0.1.0", + org.dataflowanalysis.analysis, + org.dataflowanalysis.analysis.dfd, + org.dataflowanalysis.converter, + org.dataflowanalysis.pcm.extension.nodecharacteristics, org.dataflowanalysis.analysis.pcm, org.palladiosimulator.commons.stoex;bundle-version="5.2.1", de.uka.ipd.sdq.stoex.analyser;bundle-version="5.2.1", diff --git a/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/pom.xml b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/pom.xml new file mode 100644 index 00000000..c9fafa42 --- /dev/null +++ b/backend/analysisBackendServer/bundles/org.dataflowanalysis.standalone/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + bundles-parent + 3.0.0-SNAPSHOT + ../pom.xml + + org.dataflowanalysis.standalone + eclipse-plugin + 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..f929f009 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,40 +1,21 @@ package org.dataflowanalysis.standalone; -import java.util.Scanner; - import org.dataflowanalysis.standalone.websocket.WebSocketServerUtils; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; public class Application implements IApplication { - @Override - public Object start(IApplicationContext context) throws Exception { - try { - Thread webSocketServer = WebSocketServerUtils.startWebSocketServer(); - - 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(); + @Override + public Object start(IApplicationContext context) throws Exception { + try { + Thread webSocketServer = WebSocketServerUtils.startWebSocketServer(); + webSocketServer.join(); // block until server thread dies } catch (Exception e) { e.printStackTrace(); - } - return IApplication.EXIT_OK; - } - - @Override - public void stop() { - - } -} + } + return IApplication.EXIT_OK; + } + @Override + public void stop() { + } +} \ No newline at end of file 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..5706c92f 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,6 +1,7 @@ package org.dataflowanalysis.standalone; import org.dataflowanalysis.standalone.websocket.WebSocketServerUtils; +import org.dataflowanalysis.analysis.DataFlowConfidentialityAnalysis; public class Main { public static void main(String[] args) { 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 index 76f3c130..026566d0 100644 --- 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 @@ -89,7 +89,7 @@ public static WebEditorDfd analyzeAnnotate(WebEditorDfd 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(); @@ -98,8 +98,9 @@ public static WebEditorDfd analyzeAnnotate(WebEditorDfd webEditorDfd) { oldNode.annotations().removeAll(annotationsToRemove); oldNode.annotations().addAll(child.annotations()); } - } - return webEditorDfd; + } + + return webEditorDfd.withViolations(newJson.violations()); } /** diff --git a/backend/analysisBackendServer/bundles/pom.xml b/backend/analysisBackendServer/bundles/pom.xml new file mode 100644 index 00000000..947a1a58 --- /dev/null +++ b/backend/analysisBackendServer/bundles/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + parent + 3.0.0-SNAPSHOT + ../pom.xml + + bundles-parent + pom + + org.dataflowanalysis.standalone + + diff --git a/backend/analysisBackendServer/features/org.dataflowanalysis.standalone.feature/feature.xml b/backend/analysisBackendServer/features/org.dataflowanalysis.standalone.feature/feature.xml index b0c99572..0f2bebd7 100644 --- a/backend/analysisBackendServer/features/org.dataflowanalysis.standalone.feature/feature.xml +++ b/backend/analysisBackendServer/features/org.dataflowanalysis.standalone.feature/feature.xml @@ -2,7 +2,7 @@ + + 4.0.0 + + org.dataflowanalysis.standalone + features-parent + 3.0.0-SNAPSHOT + ../pom.xml + + org.dataflowanalysis.standalone.feature + eclipse-feature + diff --git a/backend/analysisBackendServer/features/pom.xml b/backend/analysisBackendServer/features/pom.xml new file mode 100644 index 00000000..8b65d3ba --- /dev/null +++ b/backend/analysisBackendServer/features/pom.xml @@ -0,0 +1,15 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + parent + 3.0.0-SNAPSHOT + ../pom.xml + + features-parent + pom + + org.dataflowanalysis.standalone.feature + + diff --git a/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.product/org.dataflowanalysis.standalone.product b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.product/org.dataflowanalysis.standalone.product new file mode 100644 index 00000000..9836cc01 --- /dev/null +++ b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.product/org.dataflowanalysis.standalone.product @@ -0,0 +1,50 @@ + + + + + + + + + -XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.product/pom.xml b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.product/pom.xml new file mode 100644 index 00000000..57e8b849 --- /dev/null +++ b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.product/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + releng-parent + 3.0.0-SNAPSHOT + ../pom.xml + + org.dataflowanalysis.standalone.product + eclipse-repository + + + + + org.eclipse.tycho + tycho-p2-director-plugin + ${tycho.version} + + + materialize-products + + materialize-products + + + + archive-products + + archive-products + + + + + + + org.dataflowanalysis.standalone.product + standalone-dfa-server + + + + + + + diff --git a/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/org.dataflowanalysis.standalone.targetplatform.targetplatform.target b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/org.dataflowanalysis.standalone.targetplatform.targetplatform.target index b738025e..7cb04a7d 100644 --- a/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/org.dataflowanalysis.standalone.targetplatform.targetplatform.target +++ b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/org.dataflowanalysis.standalone.targetplatform.targetplatform.target @@ -2,6 +2,11 @@ + + + + + @@ -144,10 +149,8 @@ - - - - + + diff --git a/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/pom.xml b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/pom.xml new file mode 100644 index 00000000..0c98b370 --- /dev/null +++ b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.targetplatform/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + releng-parent + 3.0.0-SNAPSHOT + ../pom.xml + + org.dataflowanalysis.standalone.targetplatform + eclipse-target-definition + diff --git a/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.updatesite/pom.xml b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.updatesite/pom.xml new file mode 100644 index 00000000..9680cdb7 --- /dev/null +++ b/backend/analysisBackendServer/releng/org.dataflowanalysis.standalone.updatesite/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + releng-parent + 3.0.0-SNAPSHOT + ../pom.xml + + org.dataflowanalysis.standalone.updatesite + eclipse-repository + diff --git a/backend/analysisBackendServer/releng/pom.xml b/backend/analysisBackendServer/releng/pom.xml new file mode 100644 index 00000000..60d93f45 --- /dev/null +++ b/backend/analysisBackendServer/releng/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + org.dataflowanalysis.standalone + parent + 3.0.0-SNAPSHOT + ../pom.xml + + releng-parent + pom + + org.dataflowanalysis.standalone.targetplatform + org.dataflowanalysis.standalone.updatesite + org.dataflowanalysis.standalone.product + + diff --git a/backend/analysisBackendServer/violationSummarySetup.md b/backend/analysisBackendServer/violationSummarySetup.md new file mode 100644 index 00000000..4e8a2c2a --- /dev/null +++ b/backend/analysisBackendServer/violationSummarySetup.md @@ -0,0 +1,100 @@ +# Standalone DFA WebSocket Backend + +> **CAUTION:** This application exposes a WebSocket server to the network. +> If you want to run it locally only, adjust the WebSocket host accordingly. + +## Overview + +This is a WebSocket server that exposes the DataFlowAnalysis backend +for use with the xDECAF web editor frontend. It is built using Tycho/Maven and +can be deployed headlessly. + +## Prerequisites + +- Java 17 +- Maven 3.9+ +- Git + +## Development Setup + +For local development without building a headless product: + +- Clone DataFlowAnalysis repo and import bundles in Eclipse Product +- Clone this repository and import in Eclipse Product +- Set project target platform to active target platform +- Run `Main` + +## Build + +**Important:** Before building, update the local repository path in: +`releng/org.dataflowanalysis.standalone.targetplatform/org.dataflowanalysis.standalone.targetplatform.targetplatform.target` + +Change the `file://` location to point to your local DataFlowAnalysis build output: +```xml + +``` + +The standalone backend depends on modified bundles from the +[DataFlowAnalysis](https://github.com/dalu-wins/DataFlowAnalysis) repository. +You must build that first. + +**Step 1 — Build the modified DataFlowAnalysis bundles:** +```bash +git clone https://github.com/dalu-wins/DataFlowAnalysis.git +cd DataFlowAnalysis +mvn clean install -Dtycho.localArtifacts=ignore -Dtycho.target.eager=true +``` + +**Step 2 — Build the standalone backend:** +```bash +cd OnlineEditor/backend/analysisBackendServer +mvn clean package -Dtycho.localArtifacts=ignore -Dtycho.target.eager=true +``` + +The deployable archive will be at: +``` +releng/org.dataflowanalysis.standalone.product/target/products/standalone-dfa-server-linux.gtk.x86_64.tar.gz +``` + +## Deploy + +Copy the archive to your server and extract it: +```bash +tar -xzf standalone-dfa-server-linux.gtk.x86_64.tar.gz +``` + +Run the server: +```bash +./standalone-dfa-server -application org.dataflowanalysis.standalone.application -noSplash -consoleLog +``` + +The WebSocket server will start on port 3000 at `/events`. + +## Run as a systemd service + +Create `/etc/systemd/system/dfa-server.service`: +```ini +[Unit] +Description=DataFlow Analysis WebSocket Server +After=network.target + +[Service] +Type=simple +User=youruser +WorkingDirectory=/path/to/standalone-dfa-server +ExecStart=/path/to/standalone-dfa-server -application org.dataflowanalysis.standalone.application -noSplash -consoleLog +Restart=on-failure +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +``` + +Then enable and start: +```bash +sudo systemctl daemon-reload +sudo systemctl enable dfa-server +sudo systemctl start dfa-server +``` \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 00000000..aba25f73 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "frontend", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/frontend/webEditor/src/diagrams/dacDiagram.json b/frontend/webEditor/src/diagrams/dacDiagram.json new file mode 100644 index 00000000..19f4fe7a --- /dev/null +++ b/frontend/webEditor/src/diagrams/dacDiagram.json @@ -0,0 +1,883 @@ +{ + "model": { + "canvasBounds": { + "x": 0, + "y": 0, + "width": 2333.75, + "height": 1168.75 + }, + "scroll": { + "x": -105.89197860962565, + "y": -63 + }, + "zoom": 3.0837730870712403, + "position": { + "x": 0, + "y": 0 + }, + "size": { + "width": -1, + "height": -1 + }, + "features": {}, + "type": "graph", + "id": "root", + "children": [ + { + "position": { + "x": 222, + "y": 75 + }, + "size": { + "width": 71, + "height": 42 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Mother", + "labels": [ + { + "labelTypeId": "vljvh", + "labelTypeValueId": "z2vuaa" + } + ], + "ports": [ + { + "position": { + "x": -3.5, + "y": 17.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "6bvsh7", + "type": "port:dfd-input", + "children": [] + }, + { + "position": { + "x": 67.5, + "y": 17.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "set TraversedNodes.mother", + "features": {}, + "id": "pzv6hg", + "type": "port:dfd-output", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "3lqxlo", + "type": "node:input-output", + "children": [] + }, + { + "position": { + "x": 226.5, + "y": 199 + }, + "size": { + "width": 62, + "height": 42 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Dad", + "labels": [ + { + "labelTypeId": "vljvh", + "labelTypeValueId": "oqq2r" + } + ], + "ports": [ + { + "position": { + "x": -3.5, + "y": 17.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "rva68j", + "type": "port:dfd-input", + "children": [] + }, + { + "position": { + "x": 58.5, + "y": 17.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward picture\nset TraversedNodes.dad", + "features": {}, + "id": "f6wz2q", + "type": "port:dfd-output", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "wocqg", + "type": "node:input-output", + "children": [] + }, + { + "position": { + "x": 211, + "y": 137 + }, + "size": { + "width": 63, + "height": 42 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Aunt", + "labels": [ + { + "labelTypeId": "vljvh", + "labelTypeValueId": "vb0xw" + } + ], + "ports": [ + { + "position": { + "x": -3.5, + "y": 17.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "j6a32", + "type": "port:dfd-input", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "mhu9ma", + "type": "node:input-output", + "children": [] + }, + { + "position": { + "x": 570, + "y": 99.41666666666666 + }, + "size": { + "width": 125, + "height": 78 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Family Pictures", + "labels": [ + { + "labelTypeId": "9jr84l", + "labelTypeValueId": "01mazd" + }, + { + "labelTypeId": "6drw8l", + "labelTypeValueId": "dhfohg" + }, + { + "labelTypeId": "6drw8l", + "labelTypeValueId": "qsj85" + }, + { + "labelTypeId": "6drw8l", + "labelTypeValueId": "7p1lcu" + } + ], + "ports": [ + { + "position": { + "x": -3.5, + "y": 49.66666666666667 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "efr68k", + "type": "port:dfd-input", + "children": [] + }, + { + "position": { + "x": -3.5, + "y": 21.333333333333336 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward picture\nset TraversedNodes.pictureStorage\nset Read.Aunt", + "features": {}, + "id": "l7yqhg", + "type": "port:dfd-output", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "050esr", + "type": "node:storage", + "children": [] + }, + { + "position": { + "x": 211, + "y": 12 + }, + "size": { + "width": 100, + "height": 42 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Indexing Bot", + "labels": [ + { + "labelTypeId": "vljvh", + "labelTypeValueId": "1cnzie" + } + ], + "ports": [ + { + "position": { + "x": -3.5, + "y": 17.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "h5c7l", + "type": "port:dfd-input", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "fagty", + "type": "node:input-output", + "children": [] + }, + { + "position": { + "x": 12, + "y": 78 + }, + "size": { + "width": 105, + "height": 36 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Read Pictures", + "labels": [], + "ports": [ + { + "position": { + "x": 101.5, + "y": 7.333333333333332 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "hmhlg5", + "type": "port:dfd-input", + "children": [] + }, + { + "position": { + "x": 101.5, + "y": 14.499999999999998 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward picture\nset TraversedNodes.readPicture", + "features": {}, + "id": "9fv3si", + "type": "port:dfd-output", + "children": [] + }, + { + "position": { + "x": 101.5, + "y": 21.666666666666664 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward picture\nset TraversedNodes.readPicture", + "features": {}, + "id": "b4g3ml", + "type": "port:dfd-output", + "children": [] + }, + { + "position": { + "x": 101.5, + "y": 28.83333333333333 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward picture\nset TraversedNodes.readPicture", + "features": {}, + "id": "tj9ox", + "type": "port:dfd-output", + "children": [] + }, + { + "position": { + "x": 101.5, + "y": 0.16666666666666607 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward picture\nset TraversedNodes.readPicture", + "features": {}, + "id": "j4hq8ov", + "type": "port:dfd-output", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "2ksl0i", + "type": "node:function", + "children": [] + }, + { + "routingPoints": [ + { + "x": 563, + "y": 124.25 + }, + { + "x": 543, + "y": 124.25 + }, + { + "x": 543, + "y": 64 + }, + { + "x": 154, + "y": 64 + }, + { + "x": 154, + "y": 88.83333333333333 + }, + { + "x": 124, + "y": 88.83333333333333 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "picture", + "features": {}, + "id": "xw3tf", + "type": "edge:arrow", + "sourceId": "l7yqhg", + "targetId": "hmhlg5", + "labels": null, + "ports": null, + "children": [] + }, + { + "routingPoints": [ + { + "x": 124, + "y": 96 + }, + { + "x": 215, + "y": 96 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "picture", + "features": {}, + "id": "wr13pw", + "type": "edge:arrow", + "sourceId": "9fv3si", + "targetId": "6bvsh7", + "labels": null, + "ports": null, + "children": [] + }, + { + "routingPoints": [ + { + "x": 124, + "y": 103.16666666666666 + }, + { + "x": 154, + "y": 103.16666666666666 + }, + { + "x": 154, + "y": 158 + }, + { + "x": 204, + "y": 158 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "picture", + "features": {}, + "id": "osynnp", + "type": "edge:arrow", + "sourceId": "b4g3ml", + "targetId": "j6a32", + "labels": null, + "ports": null, + "children": [] + }, + { + "routingPoints": [ + { + "x": 124, + "y": 110.33333333333333 + }, + { + "x": 144, + "y": 110.33333333333333 + }, + { + "x": 144, + "y": 220 + }, + { + "x": 219.5, + "y": 220 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "picture", + "features": {}, + "id": "ymvkcs", + "type": "edge:arrow", + "sourceId": "tj9ox", + "targetId": "rva68j", + "labels": null, + "ports": null, + "children": [] + }, + { + "routingPoints": [ + { + "x": 124, + "y": 81.66666666666667 + }, + { + "x": 144, + "y": 81.66666666666667 + }, + { + "x": 144, + "y": 33 + }, + { + "x": 204, + "y": 33 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "picture", + "features": {}, + "id": "ryd2n", + "type": "edge:arrow", + "sourceId": "j4hq8ov", + "targetId": "h5c7l", + "labels": null, + "ports": null, + "children": [] + }, + { + "position": { + "x": 388, + "y": 140 + }, + "size": { + "width": 98, + "height": 36 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "Add Pictures", + "labels": [], + "ports": [ + { + "position": { + "x": 94.5, + "y": 14.5 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "behavior": "forward mother_picture\nset TraversedNodes.addPicture", + "features": {}, + "id": "i75rub", + "type": "port:dfd-output", + "children": [] + }, + { + "position": { + "x": -3.5, + "y": 21.666666666666668 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "q9uoh2", + "type": "port:dfd-input", + "children": [] + }, + { + "position": { + "x": -3.5, + "y": 7.333333333333334 + }, + "size": { + "width": 7, + "height": 7 + }, + "strokeWidth": 0, + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "features": {}, + "id": "y7wy2c", + "type": "port:dfd-input", + "children": [] + } + ], + "hideLabels": false, + "minimumWidth": 50, + "features": {}, + "id": "d2erj", + "type": "node:function", + "children": [] + }, + { + "routingPoints": [ + { + "x": 493, + "y": 158 + }, + { + "x": 543, + "y": 158 + }, + { + "x": 543, + "y": 152.58333333333331 + }, + { + "x": 563, + "y": 152.58333333333331 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "picture", + "features": {}, + "id": "c9na5t", + "type": "edge:arrow", + "sourceId": "i75rub", + "targetId": "efr68k", + "labels": null, + "ports": null, + "children": [] + }, + { + "routingPoints": [ + { + "x": 295.5, + "y": 220 + }, + { + "x": 361, + "y": 220 + }, + { + "x": 361, + "y": 165.16666666666666 + }, + { + "x": 381, + "y": 165.16666666666666 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "dad_picture", + "features": {}, + "id": "pd8t5y", + "type": "edge:arrow", + "sourceId": "f6wz2q", + "targetId": "q9uoh2", + "labels": null, + "ports": null, + "children": [] + }, + { + "routingPoints": [ + { + "x": 300, + "y": 96 + }, + { + "x": 361, + "y": 96 + }, + { + "x": 361, + "y": 150.83333333333334 + }, + { + "x": 381, + "y": 150.83333333333334 + } + ], + "selected": false, + "hoverFeedback": false, + "opacity": 1, + "text": "mother_picture", + "features": {}, + "id": "ekx5wq", + "type": "edge:arrow", + "sourceId": "pzv6hg", + "targetId": "y7wy2c", + "labels": null, + "ports": null, + "children": [] + } + ] + }, + "labelTypes": [ + { + "id": "vljvh", + "name": "Identity", + "values": [ + { + "id": "z2vuaa", + "text": "Mother" + }, + { + "id": "oqq2r", + "text": "Dad" + }, + { + "id": "vb0xw", + "text": "Aunt" + }, + { + "id": "1cnzie", + "text": "IndexingBot" + } + ] + }, + { + "id": "6drw8l", + "name": "Read", + "values": [ + { + "id": "7p1lcu", + "text": "Mother" + }, + { + "id": "qsj85", + "text": "Dad" + }, + { + "id": "dhfohg", + "text": "Aunt" + }, + { + "id": "e8kf57", + "text": "IndexingBot" + } + ] + }, + { + "id": "9jr84l", + "name": "Owner", + "values": [ + { + "id": "01mazd", + "text": "Mother" + } + ] + }, + { + "id": "4qmig", + "name": "TraversedNodes", + "values": [ + { + "id": "6p4cbg", + "text": "addPicture" + }, + { + "id": "igu1hs", + "text": "pictureStorage" + }, + { + "id": "yqu7nt", + "text": "readPicture" + }, + { + "id": "huqgc6", + "text": "mother" + }, + { + "id": "as8h9i", + "text": "dad" + }, + { + "id": "0noedq", + "text": "aunt" + }, + { + "id": "33ryia", + "text": "indexingBot" + } + ] + } + ], + "constraints": [ + { + "name": "Isolation", + "constraint": "data !Read.IndexingBot neverFlows vertex Identity.IndexingBot" + } + ], + "mode": "edit", + "version": 1 +} diff --git a/frontend/webEditor/src/index.ts b/frontend/webEditor/src/index.ts index 17c67cc1..794450a2 100644 --- a/frontend/webEditor/src/index.ts +++ b/frontend/webEditor/src/index.ts @@ -26,6 +26,7 @@ import { assignmentModule } from "./assignment/di.config"; import { editorModeOverwritesModule } from "./editModeOverwrites/di.config"; import { loadingIndicatorModule } from "./loadingIndicator/di.config"; import { keyListenerModule } from "./keyListeners/di.config"; +import { violationUiModule } from "./violationUi/di.config"; const container = new Container(); @@ -55,6 +56,7 @@ container.load( editorModeOverwritesModule, loadingIndicatorModule, keyListenerModule, + violationUiModule, ); const startUpAgents = container.getAll(StartUpAgent); diff --git a/frontend/webEditor/src/serialize/SavedDiagram.ts b/frontend/webEditor/src/serialize/SavedDiagram.ts index 623b8752..7979c5d5 100644 --- a/frontend/webEditor/src/serialize/SavedDiagram.ts +++ b/frontend/webEditor/src/serialize/SavedDiagram.ts @@ -2,6 +2,7 @@ import { SModelRoot } from "sprotty-protocol"; import { Constraint } from "../constraint/Constraint"; import { LabelType } from "../labels/LabelType"; import { EditorMode } from "../settings/editorMode"; +import { Violation } from "../violationUi/Violation"; export interface SavedDiagram { model: SModelRoot; @@ -9,5 +10,6 @@ export interface SavedDiagram { constraints?: Constraint[]; mode?: EditorMode; version: number; + violations?: Violation[]; } export const CURRENT_VERSION = 1; diff --git a/frontend/webEditor/src/serialize/analyze.ts b/frontend/webEditor/src/serialize/analyze.ts index 6b949abe..951f0264 100644 --- a/frontend/webEditor/src/serialize/analyze.ts +++ b/frontend/webEditor/src/serialize/analyze.ts @@ -10,6 +10,7 @@ import { EditorModeController } from "../settings/editorMode"; import { Action } from "sprotty-protocol"; import { ConstraintRegistry } from "../constraint/constraintRegistry"; import { LoadingIndicator } from "../loadingIndicator/loadingIndicator"; +import { ViolationService } from "../violationUi/violationService"; export namespace AnalyzeAction { export const KIND = "analyze"; @@ -31,6 +32,7 @@ export class AnalyzeCommand extends LoadJsonCommand { @inject(DfdWebSocket) private readonly dfdWebSocket: DfdWebSocket, @inject(TYPES.IActionDispatcher) actionDispatcher: ActionDispatcher, @inject(LoadingIndicator) loadingIndicator: LoadingIndicator, + @inject(ViolationService) private violationService: ViolationService, ) { super( logger, @@ -44,13 +46,22 @@ export class AnalyzeCommand extends LoadJsonCommand { } protected async getFile(context: CommandExecutionContext): Promise | undefined> { - const savedDiagram = { + const savedDiagram: SavedDiagram = { model: context.modelFactory.createSchema(context.root), labelTypes: this.labelTypeRegistry.getLabelTypes(), constraints: this.constraintRegistry.getConstraintList(), mode: this.editorModeController.get(), version: CURRENT_VERSION, }; - return await this.dfdWebSocket.requestDiagram("Json:" + JSON.stringify(savedDiagram)); + + const response = await this.dfdWebSocket.requestDiagram("Json:" + JSON.stringify(savedDiagram)); + + if (response && response.content) { + const violations = response.content.violations || []; + this.violationService.updateViolations(violations); + response.content.violations = violations; + } + + return response; } } diff --git a/frontend/webEditor/src/serialize/loadDefaultDiagram.ts b/frontend/webEditor/src/serialize/loadDefaultDiagram.ts index 3e5c889b..4152909d 100644 --- a/frontend/webEditor/src/serialize/loadDefaultDiagram.ts +++ b/frontend/webEditor/src/serialize/loadDefaultDiagram.ts @@ -1,6 +1,6 @@ import { FileData, LoadJsonCommand } from "./loadJson"; -import defaultDiagram from "./defaultDiagram.json"; import { SavedDiagram } from "./SavedDiagram"; +import defaultDiagram from "./defaultDiagram.json" import { Action } from "sprotty-protocol"; import { inject } from "inversify"; import { TYPES, ILogger, ActionDispatcher } from "sprotty"; @@ -45,8 +45,9 @@ export class LoadDefaultDiagramCommand extends LoadJsonCommand { protected async getFile(): Promise | undefined> { return { - fileName: "diagram.json", + fileName: "Online Shop", content: defaultDiagram as SavedDiagram, }; } + } diff --git a/frontend/webEditor/src/violationUi/Violation.ts b/frontend/webEditor/src/violationUi/Violation.ts new file mode 100644 index 00000000..34fb067d --- /dev/null +++ b/frontend/webEditor/src/violationUi/Violation.ts @@ -0,0 +1,6 @@ +export interface Violation { + constraint: string; + tfg: string[]; + violatedVertices: string[]; + inducingVertices: string[]; +} \ No newline at end of file diff --git a/frontend/webEditor/src/violationUi/di.config.ts b/frontend/webEditor/src/violationUi/di.config.ts new file mode 100644 index 00000000..8dca8fb7 --- /dev/null +++ b/frontend/webEditor/src/violationUi/di.config.ts @@ -0,0 +1,12 @@ +import { ContainerModule } from "inversify"; +import { TYPES } from "sprotty"; +import { ViolationUI } from "./violationUi"; +import { EDITOR_TYPES } from "../editorTypes"; +import { ViolationService } from "./violationService"; + +export const violationUiModule = new ContainerModule((bind) => { + bind(ViolationUI).toSelf().inSingletonScope(); + bind(TYPES.IUIExtension).toService(ViolationUI); + bind(EDITOR_TYPES.DefaultUIElement).toService(ViolationUI); + bind(ViolationService).toSelf().inSingletonScope(); +}); diff --git a/frontend/webEditor/src/violationUi/violationService.ts b/frontend/webEditor/src/violationUi/violationService.ts new file mode 100644 index 00000000..47d9d037 --- /dev/null +++ b/frontend/webEditor/src/violationUi/violationService.ts @@ -0,0 +1,21 @@ +import { injectable } from "inversify"; +import { Violation } from "./Violation"; + +@injectable() +export class ViolationService { + private violations: Violation[] = []; + private listeners: ((violations: Violation[]) => void)[] = []; + + updateViolations(newViolations: Violation[]) { + this.violations = newViolations; + this.listeners.forEach((callback) => callback(this.violations)); + } + + onViolationsChanged(callback: (violations: Violation[]) => void) { + this.listeners.push(callback); + } + + getViolations() { + return this.violations; + } +} diff --git a/frontend/webEditor/src/violationUi/violationUi.css b/frontend/webEditor/src/violationUi/violationUi.css new file mode 100644 index 00000000..e01d2f41 --- /dev/null +++ b/frontend/webEditor/src/violationUi/violationUi.css @@ -0,0 +1,100 @@ +.violation-ui { + right: 20px; + bottom: 70px; + padding: 10px; + max-width: 30vw; + overflow: visible; + + .violation-content { + width: 100%; + display: block; + } + + .summary-text { + width: 100%; + max-height: 250px; + overflow-y: auto; + overflow-x: hidden; + } + + .violation-list { + width: 100%; + } + + .violation-item { + padding: 12px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); + width: 100%; + } + + .violation-table { + width: 100%; + table-layout: auto; + border-collapse: collapse; + } + + .violation-table td { + padding: 4px 8px; + vertical-align: top; + font-size: 12px; + word-break: break-all; + overflow-wrap: anywhere; + white-space: normal; + } + + .violation-table td:first-child { + font-weight: bold; + color: var(--sprotty-label-color); + white-space: nowrap; + } + + .status-info { + display: block; + width: 100%; + color: #636e72; + font-style: italic; + font-size: 0.9em; + line-height: 1.4; + text-align: center; + padding: 20px 10px; + box-sizing: border-box; + margin: 0 auto; + } + + .help-icon { + display: inline-block; + width: 16px; + height: 16px; + margin-left: 4px; + vertical-align: text-top; + cursor: help; + position: relative; + } + + .help-icon::before { + content: ""; + background-image: url("@fortawesome/fontawesome-free/svgs/regular/circle-question.svg"); + display: inline-block; + filter: invert(var(--dark-mode)); + height: 16px; + width: 16px; + background-size: 16px 16px; + } + + .help-icon:hover::after { + content: attr(data-tooltip); + position: fixed; + background: #333; + color: #fff; + padding: 4px 8px; + border-radius: 4px; + font-size: 11px; + white-space: normal; + word-break: normal; + overflow-wrap: break-word; + max-width: 200px; + z-index: 9999; + pointer-events: none; + margin-left: 8px; + } +} \ No newline at end of file diff --git a/frontend/webEditor/src/violationUi/violationUi.ts b/frontend/webEditor/src/violationUi/violationUi.ts new file mode 100644 index 00000000..6c8243b8 --- /dev/null +++ b/frontend/webEditor/src/violationUi/violationUi.ts @@ -0,0 +1,87 @@ +import { injectable, inject } from "inversify"; +import { ViolationService } from "./violationService"; +import "./violationUi.css"; +import { AccordionUiExtension } from "../accordionUiExtension"; +import { Violation } from "./Violation"; + +@injectable() +export class ViolationUI extends AccordionUiExtension { + static readonly ID = "violation-ui"; + + constructor(@inject(ViolationService) private violationService: ViolationService) { + super("left", "up"); + } + + id() { + return ViolationUI.ID; + } + + containerClass() { + return ViolationUI.ID; + } + + protected initializeHeaderContent(headerElement: HTMLElement) { + headerElement.innerText = "Violation Summary"; + } + + protected initializeHidableContent(contentElement: HTMLElement) { + contentElement.innerHTML = ` +
+
+
+

+ No violation data found. Run an Analysis first. +

+
+
+
+ `; + + this.violationService.onViolationsChanged((violations) => { + this.updateSimpleTab(contentElement, violations); + }); + } + + private updateSimpleTab(container: HTMLElement, violations: Violation[]) { + const simplePane = container.querySelector("#simple-summary .summary-text"); + if (!simplePane) return; + + if (violations.length === 0) { + simplePane.innerHTML = `

No violations found. Everything looks good!

`; + return; + } + + const listItems = violations + .map( + (v) => ` +
+ + + + + + + + + + + + + + + + + +
Constraint${v.constraint}
Violation in${v.violatedVertices.join(", ")}
+ Induced by + ${v.inducingVertices.join(", ")}
Flow Graph ${v.tfg.join(", ")}
+
+ `, + ) + .join(""); + + simplePane.innerHTML = ` +
${listItems}
+ `; + } +} \ No newline at end of file diff --git a/frontend/webEditor/src/webSocket/webSocket.ts b/frontend/webEditor/src/webSocket/webSocket.ts index 8811c528..61b4bfad 100644 --- a/frontend/webEditor/src/webSocket/webSocket.ts +++ b/frontend/webEditor/src/webSocket/webSocket.ts @@ -10,7 +10,9 @@ export class DfdWebSocket { resolve?: (v: string) => void; reject?: (e: Error) => void; } = {}; - private static readonly WS_URL = "wss://websocket.dataflowanalysis.org/events/"; + //private static readonly WS_URL = "ws://localhost:3000/events/"; + private static readonly WS_URL = "wss://xdecaf.dalu-wins.de/websocket"; + constructor( @inject(TYPES.ILogger) private readonly logger: ILogger,