From 063b09d64daab208eda6ad33f747fd3a67f282b8 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 10 May 2018 09:37:29 +0200 Subject: [PATCH 01/76] Before branching from dev --- tools/mqtt.jsap | 24 +++++++++---------- .../arces/wot/sepa/apps/mqtt/MQTTAdapter.java | 7 +++++- .../unibo/arces/wot/sepa/tools/Dashboard.java | 2 +- tools/src/main/resources/log4j2.xml | 2 +- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/tools/mqtt.jsap b/tools/mqtt.jsap index 4ca5d8ad..74e21be3 100644 --- a/tools/mqtt.jsap +++ b/tools/mqtt.jsap @@ -1,5 +1,5 @@ { - "host": "localhost", + "host": "mml.arces.unibo.it", "sparql11protocol": { "protocol": "http", "port": 8000, @@ -45,8 +45,8 @@ "extended": { "simulate": false, "mqtt": { - "url": "giove.arces.unibo.it", - "port": 52877, + "url": "test.mosquitto.org", + "port": 1883, "topics": [ "#" ], @@ -511,7 +511,7 @@ }, "updates": { "MQTT_MESSAGE": { - "sparql": "DELETE {?node mqtt:hasValue ?oldValue } INSERT {?node rdf:type mqtt:Node ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker } WHERE { {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Node-\",STRUUID())) AS ?node) . FILTER NOT EXISTS {?nodeOld rdf:type mqtt:Node ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker } } UNION { ?node mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:hasValue ?oldValue ; rdf:type mqtt:Node } }", + "sparql": "INSERT {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?timestamp} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Message-\",STRUUID())) AS ?message) . BIND(now() AS ?timestamp)}", "forcedBindings": { "value": { "type": "literal", @@ -604,14 +604,14 @@ } } }, - "COUNT_TOPICS": { - "sparql": "SELECT (COUNT(?topic) AS ?topics) WHERE {?node rdf:type mqtt:Node ; mqtt:hasTopic ?topic}" + "MQTT_TOPICS_COUNT": { + "sparql": "SELECT (COUNT(DISTINCT ?topic) AS ?topics) WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic}" }, - "TOPIC": { - "sparql": "SELECT ?topic WHERE {?node rdf:type mqtt:Node ; mqtt:hasTopic ?topic}" + "MQTT_TOPICS": { + "sparql": "SELECT DISTINCT ?broker ?topic WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" }, - "VALUE": { - "sparql": "SELECT ?value WHERE {?node rdf:type mqtt:Node ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", + "MQTT_TOPIC_VALUE": { + "sparql": "SELECT ?value WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", "forcedBindings": { "topic": { "type": "literal", @@ -619,8 +619,8 @@ } } }, - "NODE": { - "sparql": "SELECT ?topic ?value WHERE {?node rdf:type mqtt:Node ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic}" + "MQTT_MESSAGES": { + "sparql": "SELECT ?broker ?topic ?value WHERE {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" } } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java index 0e23a64c..6953b844 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java @@ -32,7 +32,7 @@ import it.unibo.arces.wot.sepa.pattern.Producer; public class MQTTAdapter extends Producer implements MqttCallback { - private static final Logger logger = LogManager.getLogger("MQTT-SEPA-Adapter"); + private static final Logger logger = LogManager.getLogger(); private MqttClient mqttClient; private String[] topicsFilter = null; @@ -103,6 +103,11 @@ public void messageArrived(String topic, MqttMessage value) throws Exception { } public boolean start() { + /* + * test.mosquitto.org 1883 + * giove.arces.unibo.it 52877 + * + * */ // MQTT JsonObject mqtt = getApplicationProfile().getExtendedData().get("mqtt").getAsJsonObject(); diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index e3ca2fd4..29b02d77 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -114,7 +114,7 @@ public class Dashboard { private static final Logger logger = LogManager.getLogger("Dashboard"); - private static final String versionLabel = "SEPA Dashboard Ver 0.9.9"; + private static final String versionLabel = "SEPA Dashboard Ver 0.9.1"; private Properties appProperties = new Properties(); diff --git a/tools/src/main/resources/log4j2.xml b/tools/src/main/resources/log4j2.xml index 95f26526..a3ff3172 100644 --- a/tools/src/main/resources/log4j2.xml +++ b/tools/src/main/resources/log4j2.xml @@ -12,7 +12,7 @@ - + From caff2cafe02f55589f17455b18ae42f012cd0550 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 10 May 2018 18:44:53 +0200 Subject: [PATCH 02/76] Fixed bugs related to configuration files and defaults. The broker runs as default on a local endpoint powered by Blazegraph. --- .../commons/protocol/SPARQL11Properties.java | 16 +-- .../arces/wot/sepa/engine/core/Engine.java | 54 ++++---- .../sepa/engine/core/EngineProperties.java | 131 +++++++----------- .../websocket/SecureWebsocketServer.java | 2 +- .../protocol/websocket/WebsocketServer.java | 2 +- 5 files changed, 89 insertions(+), 116 deletions(-) diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java index 4f8fa488..bc66db1c 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java +++ b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java @@ -155,8 +155,8 @@ private void loadProperties(File jsapFile) throws SEPAPropertiesException { logger.warn("USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker"); - throw new SEPAPropertiesException(new Exception( - "USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker")); + //throw new SEPAPropertiesException(new Exception( + // "USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker")); } } @@ -186,21 +186,21 @@ public String toString() { * */ protected void defaults() { - jsap.add("host", new JsonPrimitive("mml.arces.unibo.it")); + jsap.add("host", new JsonPrimitive("localhost")); JsonObject sparql11protocol = new JsonObject(); sparql11protocol.add("protocol", new JsonPrimitive("http")); - sparql11protocol.add("port", new JsonPrimitive(8890)); + sparql11protocol.add("port", new JsonPrimitive(9999)); JsonObject query = new JsonObject(); - query.add("path", new JsonPrimitive("/sparql")); - query.add("method", new JsonPrimitive("GET")); + query.add("path", new JsonPrimitive("/blazegraph/namespace/kb/sparql")); + query.add("method", new JsonPrimitive("POST")); query.add("format", new JsonPrimitive("JSON")); sparql11protocol.add("query", query); JsonObject update = new JsonObject(); - update.add("path", new JsonPrimitive("/sparql")); - update.add("method", new JsonPrimitive("GET")); + update.add("path", new JsonPrimitive("/blazegraph/namespace/kb/sparql")); + update.add("method", new JsonPrimitive("POST")); update.add("format", new JsonPrimitive("JSON")); sparql11protocol.add("update", update); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 12e0542e..20c207d3 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -58,12 +58,13 @@ * Event Processing Architecture (SEPA) * * @author Luca Roffia (luca.roffia@unibo.it) - * @version 0.9.0 + * @version 0.9.1 */ public class Engine implements EngineMBean { private static Engine engine; private static String version = "0.9.1"; + private EngineProperties properties = null; // Scheduler request queue private final SchedulerRequestResponseQueue schedulerQueue = new SchedulerRequestResponseQueue(); @@ -187,8 +188,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException .println("# GITHUB: https://github.com/arces-wot/sepa #"); System.out .println("# WEB: http://site.unibo.it/wot #"); - System.out - .println("# WIKI: https: // github.com/arces-wot/SEPA/wiki #"); + System.out.println("# WIKI: https://github.com/arces-wot/SEPA/wiki #"); System.out .println("##########################################################################################"); @@ -197,20 +197,19 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException EngineBeans.setVersion(version); // Initialize SPARQL 1.1 SE processing service properties - EngineProperties properties = null; try { properties = new EngineProperties("engine.jpar"); } catch (SEPAPropertiesException e) { - System.err.println("Failed to load engine.jpar: "+e.getMessage()); - properties = null; + // System.err.println("Failed to load engine.jpar: "+e.getMessage()); + // properties = null; } SPARQL11Properties endpointProperties = null; try { endpointProperties = new SPARQL11Properties("endpoint.jpar"); } catch (SEPAPropertiesException e2) { - System.err.println("Failed to load endpoint.jpar: "+e2.getMessage()); - endpointProperties = null; + // System.err.println("Failed to load endpoint.jpar: "+e2.getMessage()); + // endpointProperties = null; } // OAUTH 2.0 Authorization Manager @@ -298,7 +297,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException // Protocol gates System.out.println("----------------------"); System.out.println(""); - System.out.println("SPARQL 1.1 SE Protocol (https://wot.arces.unibo.it/TR/sparql11-se-protocol/)"); + System.out.println("SPARQL 1.1 SE Protocol (https://mml.arces.unibo.it/TR/sparql11-se-protocol/)"); System.out.println("----------------------"); if (!properties.isSecure()) { @@ -347,23 +346,28 @@ public static void main(String[] args) throws SEPASecurityException, SEPAProtoco public void shutdown() { System.out.println("Stopping..."); - System.out.println("Stopping HTTP gate..."); - httpGate.shutdown(); - - System.out.println("Stopping HTTPS gate..."); - httpsGate.shutdown(); - - try { - System.out.println("Stopping WS gate..."); - wsServer.stop(wsShutdownTimeout); - } catch (InterruptedException e) { - System.err.println(e.getMessage()); + if (!properties.isSecure()) { + System.out.println("Stopping HTTP gate..."); + httpGate.shutdown(); + + try { + System.out.println("Stopping WS gate..."); + wsServer.stop(wsShutdownTimeout); + } catch (InterruptedException e) { + System.err.println(e.getMessage()); + } } - try { - System.out.println("Stopping WSS gate..."); - wssServer.stop(wsShutdownTimeout); - } catch (InterruptedException e) { - System.err.println(e.getMessage()); + + if (properties.isSecure()) { + System.out.println("Stopping HTTPS gate..."); + httpsGate.shutdown(); + + try { + System.out.println("Stopping WSS gate..."); + wssServer.stop(wsShutdownTimeout); + } catch (InterruptedException e) { + System.err.println(e.getMessage()); + } } System.out.println("Stopped...bye bye :-)"); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java index 2723bf6a..d2fe60f5 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java @@ -17,7 +17,7 @@ */ package it.unibo.arces.wot.sepa.engine.core; -import java.io.FileNotFoundException; +//import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -34,38 +34,13 @@ import org.apache.logging.log4j.LogManager; /** -{ - "parameters": { - "scheduler": { - "queueSize": 100 - }, - "processor": { - "updateTimeout": 5000, - "queryTimeout": 5000, - "maxConcurrentRequests": 5 - }, - "spu": { - "timeout": 2000 - }, - "gates": { - "secure": false, - "ports": { - "http": 8000, - "ws": 9000, - "https": 8443, - "wss": 9443 - }, - "paths": { - "update": "/update", - "query": "/query", - "subscribe": "/subscribe", - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure" - } - } - } -} + * { "parameters": { "scheduler": { "queueSize": 100 }, "processor": { + * "updateTimeout": 5000, "queryTimeout": 5000, "maxConcurrentRequests": 5 }, + * "spu": { "timeout": 2000 }, "gates": { "secure": false, "ports": { "http": + * 8000, "ws": 9000, "https": 8443, "wss": 9443 }, "paths": { "update": + * "/update", "query": "/query", "subscribe": "/subscribe", "register": + * "/oauth/register", "tokenRequest": "/oauth/token", "securePath": "/secure" } + * } } } */ public class EngineProperties { private static final Logger logger = LogManager.getLogger("EngineProperties"); @@ -106,10 +81,10 @@ public EngineProperties(String propertiesFile) throws SEPAPropertiesException { throw new SEPAPropertiesException(e1); } - logger.warn( - "USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker"); - throw new SEPAPropertiesException(new FileNotFoundException( - "USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker")); + logger.warn("USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker"); + // throw new SEPAPropertiesException(new FileNotFoundException( + // "USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again + // the broker")); } } @@ -136,18 +111,18 @@ protected void defaults() { JsonObject spu = new JsonObject(); spu.add("timeout", new JsonPrimitive(5000)); parameters.add("spu", spu); - + // Gates JsonObject gates = new JsonObject(); gates.add("secure", new JsonPrimitive(false)); - + // Ports JsonObject ports = new JsonObject(); ports.add("http", new JsonPrimitive(8000)); ports.add("https", new JsonPrimitive(8443)); ports.add("ws", new JsonPrimitive(9000)); ports.add("wss", new JsonPrimitive(9443)); - + // URI patterns JsonObject paths = new JsonObject(); paths.add("securePath", new JsonPrimitive("/secure")); @@ -157,11 +132,11 @@ protected void defaults() { paths.add("unsubscribe", new JsonPrimitive("/unsubscribe")); paths.add("register", new JsonPrimitive("/oauth/register")); paths.add("tokenRequest", new JsonPrimitive("/oauth/token")); - + gates.add("paths", paths); gates.add("ports", ports); parameters.add("gates", gates); - + properties.add("parameters", parameters); } @@ -174,9 +149,9 @@ protected void validate() throws SEPAPropertiesException { properties.get("processor").getAsJsonObject().get("maxConcurrentRequests").getAsInt(); properties.get("spu").getAsJsonObject().get("timeout").getAsInt(); - + properties.get("gates").getAsJsonObject().get("secure").getAsBoolean(); - + properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("http").getAsInt(); properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("https").getAsInt(); properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("ws").getAsInt(); @@ -189,7 +164,7 @@ protected void validate() throws SEPAPropertiesException { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("register").getAsString(); properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("tokenRequest").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("Failed to validate jpar: "+e.getMessage())); + throw new SEPAPropertiesException(new Exception("Failed to validate jpar: " + e.getMessage())); } } @@ -200,22 +175,25 @@ private void storeProperties(String propertiesFile) throws IOException { } public boolean isSecure() { - return properties.get("gates").getAsJsonObject().get("secure").getAsBoolean(); + try { + return properties.get("gates").getAsJsonObject().get("secure").getAsBoolean(); + } catch (Exception e) { + return false; + } } + public int getMaxConcurrentRequests() { try { return properties.get("processor").getAsJsonObject().get("maxConcurrentRequests").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 5; } } - + public int getUpdateTimeout() { try { return properties.get("processor").getAsJsonObject().get("updateTimeout").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 5000; } } @@ -223,8 +201,7 @@ public int getUpdateTimeout() { public int getQueryTimeout() { try { return properties.get("processor").getAsJsonObject().get("queryTimeout").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 5000; } } @@ -232,8 +209,7 @@ public int getQueryTimeout() { public int getSchedulingQueueSize() { try { return properties.get("scheduler").getAsJsonObject().get("queueSize").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 1000; } } @@ -241,8 +217,7 @@ public int getSchedulingQueueSize() { public int getWsPort() { try { return properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("ws").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 9000; } } @@ -250,8 +225,7 @@ public int getWsPort() { public int getHttpPort() { try { return properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("http").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 8000; } } @@ -259,8 +233,7 @@ public int getHttpPort() { public int getHttpsPort() { try { return properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("https").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 8443; } } @@ -268,8 +241,7 @@ public int getHttpsPort() { public int getWssPort() { try { return properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("wss").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 9443; } } @@ -277,17 +249,16 @@ public int getWssPort() { public String getUpdatePath() { try { return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("update").getAsString(); - } - catch(Exception e) { + } catch (Exception e) { return "/update"; } } public String getSubscribePath() { try { - return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("subscribe").getAsString(); - } - catch(Exception e) { + return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("subscribe") + .getAsString(); + } catch (Exception e) { return "/subscribe"; } } @@ -295,35 +266,34 @@ public String getSubscribePath() { public String getQueryPath() { try { return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("query").getAsString(); - } - catch(Exception e) { + } catch (Exception e) { return "/query"; } } public String getRegisterPath() { try { - return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("register").getAsString(); - } - catch(Exception e) { + return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("register") + .getAsString(); + } catch (Exception e) { return "/oauth/register"; } } public String getTokenRequestPath() { try { - return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("tokenRequest").getAsString(); - } - catch(Exception e) { + return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("tokenRequest") + .getAsString(); + } catch (Exception e) { return "/oauth/token"; } } public String getSecurePath() { try { - return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("securePath").getAsString(); - } - catch(Exception e) { + return properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("securePath") + .getAsString(); + } catch (Exception e) { return "/secure"; } } @@ -331,8 +301,7 @@ public String getSecurePath() { public int getSPUProcessingTimeout() { try { return properties.get("spu").getAsJsonObject().get("timeout").getAsInt(); - } - catch(Exception e) { + } catch (Exception e) { return 2000; } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index fc995349..ac54229a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -33,7 +33,7 @@ public class SecureWebsocketServer extends WebsocketServer implements SecureWebs @Override protected String getWelcomeMessage() { - return "Secure Subscribe | wss://%s:%d%s"; + return "SPARQL 1.1 Subscribe | wss://%s:%d%s"; } public SecureWebsocketServer(int port, String path, Scheduler scheduler, AuthorizationManager oauth, DependabilityManager dependabilityMng) diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index 9d0591c0..5648ad60 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -38,7 +38,7 @@ public class WebsocketServer extends WebSocketServer implements WebsocketServerM protected Scheduler scheduler; protected String getWelcomeMessage() { - return "Subscribe | ws://%s:%d%s"; + return "SPARQL 1.1 Subscribe | ws://%s:%d%s"; } protected String welcomeMessage; From cf755835a3b678d2a642e508ee3fa8c84c5586c6 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 16 May 2018 13:12:29 +0200 Subject: [PATCH 03/76] Commit to be merged for performance project --- .../commons/protocol/SPARQL11Properties.java | 16 +-- .../commons/protocol/SPARQL11Protocol.java | 131 +++++++++++------- .../sepa/commons/sparql/BindingsResults.java | 8 +- .../unibo/arces/wot/sepa/tools/Dashboard.java | 44 +++--- 4 files changed, 116 insertions(+), 83 deletions(-) diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java index bc66db1c..044cd5bb 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java +++ b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java @@ -194,22 +194,22 @@ protected void defaults() { JsonObject query = new JsonObject(); query.add("path", new JsonPrimitive("/blazegraph/namespace/kb/sparql")); - query.add("method", new JsonPrimitive("POST")); + query.add("method", new JsonPrimitive("GET")); query.add("format", new JsonPrimitive("JSON")); sparql11protocol.add("query", query); JsonObject update = new JsonObject(); update.add("path", new JsonPrimitive("/blazegraph/namespace/kb/sparql")); - update.add("method", new JsonPrimitive("POST")); + update.add("method", new JsonPrimitive("URL_ENCODED_POST")); update.add("format", new JsonPrimitive("JSON")); sparql11protocol.add("update", update); - JsonObject graphs = new JsonObject(); - graphs.add("default-graph-uri", new JsonPrimitive("http://default")); - graphs.add("named-graph-uri", new JsonPrimitive("http://default")); - graphs.add("using-graph-uri", new JsonPrimitive("http://default")); - graphs.add("using-named-graph-uri", new JsonPrimitive("http://default")); - jsap.add("graphs", graphs); +// JsonObject graphs = new JsonObject(); +// graphs.add("default-graph-uri", new JsonPrimitive("http://default")); +// graphs.add("named-graph-uri", new JsonPrimitive("http://default")); +// graphs.add("using-graph-uri", new JsonPrimitive("http://default")); +// graphs.add("using-named-graph-uri", new JsonPrimitive("http://default")); +// jsap.add("graphs", graphs); jsap.add("sparql11protocol", sparql11protocol); } diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index 6bfb28f5..b8ee6e41 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -19,6 +19,7 @@ package it.unibo.arces.wot.sepa.commons.protocol; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; @@ -27,6 +28,7 @@ import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -127,19 +129,25 @@ public SPARQL11Protocol(SPARQL11Properties properties) throws SEPAProtocolExcept * * 2.2.3 Specifying an RDF Dataset * - * SPARQL Update requests are executed against a Graph Store, a mutable container of RDF graphs - * managed by a SPARQL service. The WHERE clause of a SPARQL update DELETE/INSERT operation [UPDATE] matches - * against data in an RDF Dataset, which is a subset of the Graph Store. The RDF Dataset for an update operation - * may be specified either in the operation string itself using the USING, USING NAMED, and/or WITH keywords, - * or it may be specified via the using-graph-uri and using-named-graph-uri parameters. + * SPARQL Update requests are executed against a Graph Store, a mutable + * container of RDF graphs managed by a SPARQL service. The WHERE clause of a + * SPARQL update DELETE/INSERT operation [UPDATE] matches against data in an RDF + * Dataset, which is a subset of the Graph Store. The RDF Dataset for an update + * operation may be specified either in the operation string itself using the + * USING, USING NAMED, and/or WITH keywords, or it may be specified via the + * using-graph-uri and using-named-graph-uri parameters. * - * It is an error to supply the using-graph-uri or using-named-graph-uri parameters when using this protocol - * to convey a SPARQL 1.1 Update request that contains an operation that uses the USING, USING NAMED, or WITH clause. + * It is an error to supply the using-graph-uri or using-named-graph-uri + * parameters when using this protocol to convey a SPARQL 1.1 Update request + * that contains an operation that uses the USING, USING NAMED, or WITH clause. * - * A SPARQL Update processor should treat each occurrence of the using-graph-uri=g parameter in an update protocol operation - * as if a USING clause were included for every operation in the SPARQL 1.1 Update request. - * Similarly, a SPARQL Update processor should treat each occurrence of the using-named-graph-uri=g parameter - * in an update protocol operation as if a USING NAMED clause were included for every operation in the SPARQL 1.1 Update request. + * A SPARQL Update processor should treat each occurrence of the + * using-graph-uri=g parameter in an update protocol operation as if a USING + * clause were included for every operation in the SPARQL 1.1 Update request. + * Similarly, a SPARQL Update processor should treat each occurrence of the + * using-named-graph-uri=g parameter in an update protocol operation as if a + * USING NAMED clause were included for every operation in the SPARQL 1.1 + * Update request. * * UPDATE 2.2 update operation The response to an update request indicates * success or failure of the request via HTTP response status code. @@ -153,9 +161,9 @@ public Response update(UpdateRequest req, int timeout) { String responseBody = null; try { - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout) - .setConnectTimeout(timeout).build(); - + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) + .build(); + // Set request entity if (properties.getUpdateMethod().equals(HTTPMethod.GET)) { // *********************** @@ -163,7 +171,7 @@ public Response update(UpdateRequest req, int timeout) { // *********************** // SPARQL 1.1 Update are issued as GET requests using the "query" URL parameter // The "default-graph-uri" parameter is REQUIRED - + String query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8") + "&format=" + URLEncoder.encode(properties.getUpdateAcceptHeader(), "UTF-8"); if (properties.getDefaultGraphURI() != null) { @@ -172,8 +180,8 @@ public Response update(UpdateRequest req, int timeout) { String url; if (properties.getHttpPort() != -1) - url = "http://" + properties.getHost() + ":" + properties.getHttpPort() - + properties.getUpdatePath() + "?" + query; + url = "http://" + properties.getHost() + ":" + properties.getHttpPort() + properties.getUpdatePath() + + "?" + query; else url = "http://" + properties.getHost() + properties.getUpdatePath() + "?" + query; @@ -189,11 +197,11 @@ public Response update(UpdateRequest req, int timeout) { requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); } updatePostRequest.setEntity(requestEntity); - + updatePostRequest.setConfig(requestConfig); updateRequest = updatePostRequest; } - + // Execute HTTP request logger.debug("Execute SPARQL 1.1 UPDATE (timeout: " + timeout + " ms) " + updateRequest.toString(), timeout); @@ -327,45 +335,66 @@ public Response query(QueryRequest req, int timeout) { RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) .build(); - try { - if (properties.getQueryMethod().equals(HTTPMethod.GET)) { + if (properties.getQueryMethod().equals(HTTPMethod.GET)) { - String query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"); - if (properties.getDefaultGraphURI() != null) { + String query = null; + try { + query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + if (properties.getDefaultGraphURI() != null) { + try { query += "&default-graph-uri=" + URLEncoder.encode(properties.getDefaultGraphURI(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } + } - String url; - if (properties.getHttpPort() != -1) - url = "http://" + properties.getHost() + ":" + properties.getHttpPort() - + properties.getQueryPath() + "?" + query; - else - url = "http://" + properties.getHost() + properties.getQueryPath() + "?" + query; + String url; + if (properties.getHttpPort() != -1) + url = "http://" + properties.getHost() + ":" + properties.getHttpPort() + properties.getQueryPath() + + "?" + query; + else + url = "http://" + properties.getHost() + properties.getQueryPath() + "?" + query; - HttpGet queryGetRequest; + HttpGet queryGetRequest = null; + try{ queryGetRequest = new HttpGet(url); - queryGetRequest.setConfig(requestConfig); - queryRequest = queryGetRequest; + } + catch (IllegalArgumentException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + queryGetRequest.setConfig(requestConfig); + queryRequest = queryGetRequest; - } else { - // Set request entity - if (properties.getQueryMethod().equals(HTTPMethod.URL_ENCODED_POST)) { + } else { + // Set request entity + if (properties.getQueryMethod().equals(HTTPMethod.URL_ENCODED_POST)) { + try { requestEntity = new StringEntity("query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8")); - } else if (properties.getQueryMethod().equals(HTTPMethod.POST)) { - requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } - queryPostRequest.setEntity(requestEntity); - queryPostRequest.setConfig(requestConfig); - queryRequest = queryPostRequest; + } else if (properties.getQueryMethod().equals(HTTPMethod.POST)) { + requestEntity = new StringEntity(req.getSPARQL(), "UTF-8"); + //requestEntity.setContentEncoding("UTF-8"); } + queryPostRequest.setEntity(requestEntity); + queryPostRequest.setConfig(requestConfig); + queryRequest = queryPostRequest; + } - queryRequest.addHeader("Accept",properties.getQueryAcceptHeader()); - // Execute HTTP request - logger.debug("Execute SPARQL 1.1 QUERY (timeout: " + timeout + " ms) " + queryRequest.toString(), timeout); - timing = System.nanoTime(); + queryRequest.addHeader("Accept", properties.getQueryAcceptHeader()); + // Execute HTTP request + logger.debug("Execute SPARQL 1.1 QUERY (timeout: " + timeout + " ms) " + queryRequest.toString(), timeout); + timing = System.nanoTime(); + try { httpResponse = httpClient.execute(queryRequest); - timing = System.nanoTime() - timing; - logger.debug("QUERY_TIME (" + timing / 1000000 + " ms)"); // Status code responseCode = httpResponse.getStatusLine().getStatusCode(); @@ -375,12 +404,18 @@ public Response query(QueryRequest req, int timeout) { responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); EntityUtils.consume(responseEntity); - } catch (IOException e) { - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (ClientProtocolException e1) { + logger.error(e1.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); + } catch (IOException e1) { + logger.error(e1.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); } finally { try { if (httpResponse != null) httpResponse.close(); + timing = System.nanoTime() - timing; + logger.debug("QUERY_TIME (" + timing / 1000000 + " ms)"); } catch (IOException e) { return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java index 6578f70b..9f8ed4aa 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java +++ b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java @@ -18,9 +18,7 @@ package it.unibo.arces.wot.sepa.commons.sparql; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -61,7 +59,7 @@ public BindingsResults(JsonObject results) { * @param solutions * the solutions */ - public BindingsResults(Set varSet, List solutions) { + public BindingsResults(ArrayList varSet, List solutions) { results = new JsonObject(); JsonObject vars = new JsonObject(); @@ -114,8 +112,8 @@ public BindingsResults(BindingsResults newBindings) { * * @return the variables */ - public Set getVariables() { - Set vars = new HashSet(); + public ArrayList getVariables() { + ArrayList vars = new ArrayList(); JsonArray variables = getVariablesArray(); if (variables == null) return vars; diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index 29b02d77..afe758f6 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -35,7 +35,6 @@ import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.text.SimpleDateFormat; import javax.swing.JFrame; import javax.swing.JLabel; @@ -67,10 +66,8 @@ import javax.swing.JScrollPane; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.Properties; -import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.Vector; @@ -110,6 +107,7 @@ import javax.swing.event.ChangeEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import javax.swing.border.EtchedBorder; public class Dashboard { private static final Logger logger = LogManager.getLogger("Dashboard"); @@ -419,16 +417,17 @@ public void setResults(ARBindingsResults res, String spuid) { if (res == null) return; - Date date = new Date(); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - String timestamp = sdf.format(date); +// Date date = new Date(); +// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); +// String timestamp = sdf.format(date); - Set vars = res.getAddedBindings().getVariables(); - vars.addAll(res.getRemovedBindings().getVariables()); + ArrayList vars = res.getAddedBindings().getVariables(); + for (String var : res.getRemovedBindings().getVariables()) { + if (!vars.contains(var)) vars.add(var); + } if (!columns.containsAll(vars) || columns.size() != vars.size()) { columns.clear(); - vars.add(""); columns.addAll(vars); super.fireTableStructureChanged(); } @@ -439,17 +438,18 @@ public void setResults(ARBindingsResults res, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getBindingValue(var), sol.isLiteral(var), true)); } - row.put("", new BindingValue(timestamp, false, true)); + //row.put("", new BindingValue(timestamp, false, true)); rows.add(row); } } + if (res.getRemovedBindings() != null) { for (Bindings sol : res.getRemovedBindings().getBindings()) { HashMap row = new HashMap(); for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getBindingValue(var), sol.isLiteral(var), false)); } - row.put("", new BindingValue(timestamp, false, false)); + //row.put("", new BindingValue(timestamp, false, false)); rows.add(row); } } @@ -518,15 +518,15 @@ public void setAddedResults(BindingsResults bindingsResults, String spuid) { if (bindingsResults == null) return; - Date date = new Date(); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - String timestamp = sdf.format(date); + //Date date = new Date(); + //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + //String timestamp = sdf.format(date); - Set vars = bindingsResults.getVariables(); + ArrayList vars = bindingsResults.getVariables(); if (!columns.containsAll(vars) || columns.size() != vars.size()) { columns.clear(); - vars.add(""); + //vars.add(""); columns.addAll(vars); super.fireTableStructureChanged(); } @@ -536,7 +536,7 @@ public void setAddedResults(BindingsResults bindingsResults, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getBindingValue(var), sol.isLiteral(var), true)); } - row.put("", new BindingValue(timestamp, false, true)); + //row.put("", new BindingValue(timestamp, false, true)); rows.add(row); } @@ -856,7 +856,7 @@ public boolean isCellEditable(int row, int column) { gbc_configuration.gridy = 0; frmSepaDashboard.getContentPane().add(configuration, gbc_configuration); configuration - .setBorder(new TitledBorder(null, "Configuration", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + .setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "SPARQL 1.1 SE Protocol configuration", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); GridBagLayout gbl_configuration = new GridBagLayout(); gbl_configuration.columnWidths = new int[] { 46, 45, 31, 20, 0, 24, 0, 0, 0, 0, 0, 0, 37, 0 }; gbl_configuration.rowHeights = new int[] { 9, -25, 0 }; @@ -1067,8 +1067,8 @@ public void actionPerformed(ActionEvent e) { frmSepaDashboard.getContentPane().add(tabbedPane, gbc_tabbedPane); JPanel primitives = new JPanel(); - tabbedPane.addTab("Primitives", null, primitives, null); - primitives.setBorder(new TitledBorder(null, "Primitives", TitledBorder.LEADING, TitledBorder.TOP, null, null)); + tabbedPane.addTab("SPARQL 1.1 primitives", null, primitives, null); + primitives.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "SPARQL 1.1 Updates and Queries", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); GridBagLayout gbl_primitives = new GridBagLayout(); gbl_primitives.columnWidths = new int[] { 684, 0, 0 }; gbl_primitives.rowHeights = new int[] { 114, 115, 0, 0, 0 }; @@ -1094,7 +1094,7 @@ public void actionPerformed(ActionEvent e) { gbl_panel_4.rowWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; panel_4.setLayout(gbl_panel_4); - JLabel lblUpdates = new JLabel("UPDATEs"); + JLabel lblUpdates = new JLabel("UPDATES"); GridBagConstraints gbc_lblUpdates = new GridBagConstraints(); gbc_lblUpdates.anchor = GridBagConstraints.NORTH; gbc_lblUpdates.insets = new Insets(0, 0, 5, 0); @@ -1181,7 +1181,7 @@ public void valueChanged(ListSelectionEvent e) { gbl_panel_6.rowWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; panel_6.setLayout(gbl_panel_6); - JLabel lblSubscribes = new JLabel("SUBSCRIBEs"); + JLabel lblSubscribes = new JLabel("QUERIES"); GridBagConstraints gbc_lblSubscribes = new GridBagConstraints(); gbc_lblSubscribes.anchor = GridBagConstraints.NORTH; gbc_lblSubscribes.insets = new Insets(0, 0, 5, 0); From f3c24ebdc422581344555f7f1051deedd8752333 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 16 May 2018 16:34:45 +0200 Subject: [PATCH 04/76] Removed unsued import in SpuManager --- .../wot/sepa/engine/processing/subscriptions/SpuManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java index aa683f4a..84c728b3 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java @@ -1,6 +1,5 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import java.util.Collection; From fd84bc5a469bae9fcee8a644fbb9c1ff68015561 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 16 May 2018 18:22:44 +0200 Subject: [PATCH 05/76] Merged with dev --- .../commons/protocol/SPARQL11Protocol.java | 24 ++++++---- .../arces/wot/sepa/engine/core/Engine.java | 1 + .../core/SchedulerRequestResponseQueue.java | 45 ------------------- .../wot/sepa/engine/processing/Processor.java | 2 +- .../subscriptions/ISubscriptionProcUnit.java | 4 +- .../engine/processing/subscriptions/SPU.java | 5 +-- .../{SpuManager.java => SPUManager.java} | 4 +- .../processing/subscriptions/SPUNaive.java | 2 +- .../subscriptions/SubscribeProcessor.java | 2 +- .../processing/subscriptions/Subscriber.java | 4 +- .../processing/subscriptions/Unsubcriber.java | 6 +-- .../wot/sepa/engine/scheduling/Scheduler.java | 1 - .../SchedulerRequestResponseQueue.java | 34 ++++++++++++++ .../subscription/SPUMangerTest.java | 8 ++-- .../arces/wot/sepa/tools/SEPATestClient.java | 4 +- 15 files changed, 69 insertions(+), 77 deletions(-) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/SchedulerRequestResponseQueue.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/{SpuManager.java => SPUManager.java} (93%) create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index b8ee6e41..cb5da782 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -115,20 +115,25 @@ public SPARQL11Protocol(SPARQL11Properties properties) throws SEPAProtocolExcept * *
 	 * update via URL-encoded POST 
-	 * - HTTP Method: POST
-	 * - Query String Parameters: None
-	 * - Request Content Type: application/x-www-form-urlencoded
-	 * - Request Message Body: URL-encoded, ampersand-separated query parameters. update (exactly 1). using-graph-uri (0 or more). using-named-graph-uri (0 or more)
+	 * - Method: POST
+	 * - Parameters: None
+	 * - Content Type: application/x-www-form-urlencoded
+	 * - Body: URL-encoded, ampersand-separated query parameters. 
+	 * 	update (exactly 1). 
+	 * 	using-graph-uri (0 or more). 
+	 * 	using-named-graph-uri (0 or more)
 	 * 
 	 * update via POST directly
-	 * - HTTP Method: POST
-	 * - Query String parameters: using-graph-uri (0 or more); using-named-graph-uri (0 or more)
-	 * - Request Content Type: application/sparql-update
-	 * - Request Message Body: Unencoded SPARQL update request string
+	 * - Method: POST
+	 * - Parameters: 
+	 * 	using-graph-uri (0 or more); 
+	 * 	using-named-graph-uri (0 or more)
+	 * - Content Type: application/sparql-update
+	 * - Body: Unencoded SPARQL update request string
 	 * 
* * 2.2.3 Specifying an RDF Dataset - * + *
 	 * SPARQL Update requests are executed against a Graph Store, a mutable
 	 * container of RDF graphs managed by a SPARQL service. The WHERE clause of a
 	 * SPARQL update DELETE/INSERT operation [UPDATE] matches against data in an RDF
@@ -151,6 +156,7 @@ public SPARQL11Protocol(SPARQL11Properties properties) throws SEPAProtocolExcept
 	 * 
 	 * UPDATE 2.2 update operation The response to an update request indicates
 	 * success or failure of the request via HTTP response status code.
+	 * 
*/ public Response update(UpdateRequest req, int timeout) { StringEntity requestEntity = null; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index f3bdce00..fa48fe00 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -52,6 +52,7 @@ import it.unibo.arces.wot.sepa.engine.protocol.websocket.SecureWebsocketServer; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; +import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; /** * This class represents the SPARQL Subscription Broker (Core) of the Semantic diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/SchedulerRequestResponseQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/SchedulerRequestResponseQueue.java deleted file mode 100644 index f68ee102..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/SchedulerRequestResponseQueue.java +++ /dev/null @@ -1,45 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.core; - -import java.util.concurrent.ConcurrentLinkedQueue; - -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; - -public class SchedulerRequestResponseQueue { - private ConcurrentLinkedQueue requests = new ConcurrentLinkedQueue(); - private ConcurrentLinkedQueue responses = new ConcurrentLinkedQueue(); - - public void addRequest(ScheduledRequest req) { - requests.add(req); - synchronized(requests) { - requests.notify(); - } - } - - public ScheduledRequest waitRequest() throws InterruptedException { - ScheduledRequest req; - while((req =requests.poll()) == null) { - synchronized(requests) { - requests.wait(); - } - } - return req; - } - - public void addResponse(Response res) { - responses.add(res); - synchronized(responses) { - responses.notify(); - } - } - - public Response waitResponse() throws InterruptedException { - Response res; - while((res = responses.poll()) == null) { - synchronized(responses) { - responses.wait(); - } - } - return res; - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index 66e5dd7a..0f51f476 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -36,8 +36,8 @@ import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.EventHandler; -import it.unibo.arces.wot.sepa.engine.core.SchedulerRequestResponseQueue; import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; import org.apache.logging.log4j.LogManager; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java index 6ddc5eea..c628d99a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java @@ -7,8 +7,8 @@ public interface ISubscriptionProcUnit extends Runnable { Response init(); - //TODO: refactor getCurrentResults - BindingsResults getFirstResults(); + + BindingsResults getCurrentResults(); void terminate(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index 94e6cd20..c21b1cc0 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -101,7 +101,7 @@ public SPU(SubscribeRequest subscribe, SPARQL11Properties properties, EventHandl public abstract Response processInternal(UpdateResponse update,int timeout); @Override - public BindingsResults getFirstResults() { + public BindingsResults getCurrentResults() { return firstResults; } @@ -133,9 +133,6 @@ public void process(UpdateResponse res) { } } -// public void ping() throws IOException { -// handler.sendPing(new Ping(getUUID())); -// } @Override public void run() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java similarity index 93% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index 84c728b3..13ea3bb0 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SpuManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -7,10 +7,10 @@ import java.util.Iterator; /** - * SpuManager is a monitor class. It takes care of the SPU collection and it encapsulate filtering algorithms based + * SpuManager is a monitor class. It takes care of the SPU collection and it encapsulates filtering algorithms based * on the internal structure. */ -public class SpuManager { +public class SPUManager { private HashMap spus = new HashMap<>(); public synchronized void register(ISubscriptionProcUnit spu){ diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index c7ec1a4b..5200e428 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -70,7 +70,7 @@ public Response init() { logger.debug("First results: " + firstResults.toString()); - return new SubscribeResponse(request.getToken(), getUUID(), request.getAlias(), getFirstResults()); + return new SubscribeResponse(request.getToken(), getUUID(), request.getAlias(), getCurrentResults()); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java index 8fe92f15..00a0d5df 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java @@ -49,7 +49,7 @@ public class SubscribeProcessor implements SPUManagerMBean { private SPARQL11Properties endpointProperties; - private SpuManager spuManager = new SpuManager(); + private SPUManager spuManager = new SPUManager(); // Request queue private LinkedBlockingQueue subscribeQueue = new LinkedBlockingQueue<>(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java index 4a0f1a83..7839bce0 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -11,9 +11,9 @@ public class Subscriber extends Thread { private final Logger logger = LogManager.getLogger("Subscriber"); private final AtomicBoolean end = new AtomicBoolean(false); private final BlockingQueue subscriptionQueue; - private final SpuManager spuManager; + private final SPUManager spuManager; - public Subscriber(BlockingQueue subscriptionQueue, SpuManager manager){ + public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ super("SEPA SPU Subscriber"); this.subscriptionQueue = subscriptionQueue; spuManager = manager; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java index 31c1dcbc..162869da 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java @@ -11,15 +11,15 @@ * Unsubscriber thread. It loops over Unsubcribe Request queue and removes the SPU from * SpuManager * - * @see SpuManager + * @see SPUManager */ public class Unsubcriber extends Thread { private final Logger logger = LogManager.getLogger("Unsubscriber"); private final BlockingQueue unsubscribeQueue; - private final SpuManager spuManager; + private final SPUManager spuManager; private final AtomicBoolean end = new AtomicBoolean(false); - public Unsubcriber(BlockingQueue unsubscribeQueue, SpuManager manager){ + public Unsubcriber(BlockingQueue unsubscribeQueue, SPUManager manager){ super("SEPA SPU Unsubscriber"); this.unsubscribeQueue = unsubscribeQueue; spuManager = manager; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 09697075..419d57b6 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -35,7 +35,6 @@ import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.engine.core.SchedulerRequestResponseQueue; import it.unibo.arces.wot.sepa.engine.dependability.Timing; /** diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java new file mode 100644 index 00000000..c03b6a11 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java @@ -0,0 +1,34 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +import java.util.concurrent.LinkedBlockingQueue; + +import it.unibo.arces.wot.sepa.commons.response.Response; + +public class SchedulerRequestResponseQueue { + private LinkedBlockingQueue requests = new LinkedBlockingQueue(); + private LinkedBlockingQueue responses = new LinkedBlockingQueue(); + + public void addRequest(ScheduledRequest req) { + try { + requests.put(req); + } catch (InterruptedException e) { + return; + } + } + + public ScheduledRequest waitRequest() throws InterruptedException { + return requests.take(); + } + + public void addResponse(Response res) { + try { + responses.put(res); + } catch (InterruptedException e) { + return; + } + } + + public Response waitResponse() throws InterruptedException { + return responses.take(); + } +} diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java index c4c66495..3be54f2b 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java @@ -4,18 +4,18 @@ import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISubscriptionProcUnit; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SpuManager; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class SPUMangerTest { - private SpuManager spuManger; + private SPUManager spuManger; @Before public void Init(){ - spuManger = new SpuManager(); + spuManger = new SPUManager(); } @Test @@ -60,7 +60,7 @@ public Response init() { } @Override - public BindingsResults getFirstResults() { + public BindingsResults getCurrentResults() { return null; } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java index 5f94e829..c4d2fdf1 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java @@ -389,8 +389,8 @@ public static void main(String[] args) throws SEPAProtocolException, SEPASecurit SEPATestClient test = new SEPATestClient(new ApplicationProfile("sepatest.jsap")); test.run(); - test = new SEPATestClient(new ApplicationProfile("sepatest-secure.jsap")); - test.run(); + //test = new SEPATestClient(new ApplicationProfile("sepatest-secure.jsap")); + //test.run(); System.exit(0); } From b13f004f258be55deffb77b3fdf107a91a0f40f2 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Mon, 21 May 2018 16:32:49 +0200 Subject: [PATCH 06/76] Merged commons, client api and client pac Tested with StarDog Support for graphs JSAP properties override Fixed JMX beans on SubscribeProcessor --- client-api/pom.xml | 5 - .../wot/sepa/api/SPARQL11SEProperties.java | 83 +- .../wot/sepa/api/SPARQL11SEProtocol.java | 84 ++- .../exceptions/SEPAPropertiesException.java | 0 .../exceptions/SEPAProtocolException.java | 0 .../exceptions/SEPASecurityException.java | 0 .../commons/protocol/SPARQL11Properties.java | 127 ++-- .../commons/protocol/SPARQL11Protocol.java | 574 ++++++++++++++ .../commons/protocol/SSLSecurityManager.java | 0 .../sepa/commons/request/QueryRequest.java | 24 +- .../commons/request/RegistrationRequest.java | 0 .../wot/sepa/commons/request/Request.java | 53 +- .../commons/request/SubscribeRequest.java | 0 .../commons/request/UnsubscribeRequest.java | 0 .../sepa/commons/request/UpdateRequest.java | 37 +- .../sepa/commons/response/ErrorResponse.java | 0 .../sepa/commons/response/JWTResponse.java | 0 .../sepa/commons/response/Notification.java | 0 .../arces/wot/sepa/commons/response/Ping.java | 0 .../sepa/commons/response/QueryResponse.java | 0 .../response/RegistrationResponse.java | 0 .../wot/sepa/commons/response/Response.java | 0 .../commons/response/SubscribeResponse.java | 0 .../commons/response/UnsubscribeResponse.java | 0 .../sepa/commons/response/UpdateResponse.java | 0 .../commons/sparql/ARBindingsResults.java | 0 .../wot/sepa/commons/sparql/Bindings.java | 0 .../sepa/commons/sparql/BindingsResults.java | 0 .../wot/sepa/commons/sparql/RDFTerm.java | 0 .../wot/sepa/commons/sparql/RDFTermBNode.java | 0 .../sepa/commons/sparql/RDFTermLiteral.java | 0 .../wot/sepa/commons/sparql/RDFTermURI.java | 0 .../arces/wot/sepa/pattern/Aggregator.java | 11 +- .../wot/sepa/pattern/ApplicationProfile.java | 707 ++++++++++++++++++ .../unibo/arces/wot/sepa/pattern/Client.java | 0 .../arces/wot/sepa/pattern/Consumer.java | 4 +- .../arces/wot/sepa/pattern/GenericClient.java | 26 +- .../arces/wot/sepa/pattern/IConsumer.java | 0 .../arces/wot/sepa/pattern/IProducer.java | 0 .../arces/wot/sepa/pattern/Producer.java | 13 +- .../unibo/arces/wot/sepa/timing/Timings.java | 48 ++ .../wot/sepa/pattern/ApplicationProfile.java | 321 -------- .../commons/protocol/SPARQL11Protocol.java | 449 ----------- engine/pom.xml | 5 - .../wot/sepa/engine/bean/EngineBeans.java | 2 +- .../sepa/engine/bean/HTTPHandlerBeans.java | 29 +- ...eans.java => SubscribeProcessorBeans.java} | 2 +- .../arces/wot/sepa/engine/core/Engine.java | 2 +- .../wot/sepa/engine/dependability/Timing.java | 37 - .../wot/sepa/engine/processing/Processor.java | 4 +- .../engine/processing/QueryProcessor.java | 22 +- .../engine/processing/UpdateProcessor.java | 19 +- .../engine/processing/subscriptions/SPU.java | 4 +- .../processing/subscriptions/SPUSync.java | 4 +- .../subscriptions/SubscribeProcessor.java | 41 +- ...Bean.java => SubscribeProcessorMBean.java} | 2 +- .../processing/subscriptions/Subscriber.java | 6 +- .../processing/subscriptions/Unsubcriber.java | 6 +- .../http/handler/SPARQL11Handler.java | 20 +- .../http/handler/SPARQL11ResponseHandler.java | 20 +- .../protocol/http/handler/UpdateHandler.java | 21 +- .../wot/sepa/engine/scheduling/Scheduler.java | 9 +- .../SchedulerRequestResponseQueue.java | 12 +- engine/src/main/resources/log4j2.xml | 18 +- tools/pom.xml | 10 - .../arces/wot/sepa/apps/chat/BasicClient.java | 4 +- .../arces/wot/sepa/apps/chat/ChatClient.java | 8 +- .../wot/sepa/apps/chat/PingPongClient.java | 4 +- .../arces/wot/sepa/apps/chat/Receiver.java | 11 +- .../arces/wot/sepa/apps/chat/Remover.java | 9 +- .../wot/sepa/apps/chat/SEPAChatTest.java | 10 +- .../arces/wot/sepa/apps/chat/Sender.java | 12 +- .../arces/wot/sepa/apps/chat/Timings.java | 106 --- .../wot/sepa/apps/chat/UpdateQueryTest.java | 18 +- .../unibo/arces/wot/sepa/tools/Dashboard.java | 545 +++++++------- .../arces/wot/sepa/tools/SEPATestClient.java | 6 +- 76 files changed, 2062 insertions(+), 1532 deletions(-) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java (78%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SSLSecurityManager.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java (77%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java (67%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java (54%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/Ping.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/RegistrationResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/ARBindingsResults.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermBNode.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java (100%) rename {commons => client-api}/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java (100%) rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java (87%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java (100%) rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java (97%) rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java (74%) rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java (100%) rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java (100%) rename {client-pac-pattern => client-api}/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java (85%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java delete mode 100644 client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java delete mode 100644 commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/{SPUManagerBeans.java => SubscribeProcessorBeans.java} (98%) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Timing.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/{SPUManagerMBean.java => SubscribeProcessorMBean.java} (92%) delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Timings.java diff --git a/client-api/pom.xml b/client-api/pom.xml index e267ac78..929ed78a 100644 --- a/client-api/pom.xml +++ b/client-api/pom.xml @@ -50,11 +50,6 @@ 4.4.6 - - it.unibo.arces.wot - commons - ${revision} - diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java index b916230f..cf0b27a0 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java @@ -55,9 +55,8 @@ } }, "security": { - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure", + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", "client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs", "client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg", "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT", @@ -133,6 +132,7 @@ public SPARQL11SEProperties(String propertiesFile) throws SEPAPropertiesExceptio if (propertiesFile == null) throw new IllegalArgumentException("Argument is null"); } + /** * Instantiates a new SPARQL 11 SE properties. * @@ -159,13 +159,12 @@ public String toString() { }, "wss": { "port": 9443, - "path": "/subscribe" + "path": "/secure/subscribe" } }, "security": { - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure", + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", "client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs", "client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg", "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT", @@ -234,8 +233,6 @@ protected void validate() throws SEPAPropertiesException { .getAsString(); jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject().get("tokenRequest") .getAsString(); - jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject().get("securePath") - .getAsString(); } } catch (Exception e) { @@ -243,47 +240,24 @@ protected void validate() throws SEPAPropertiesException { } } - public String getSecurePath() { - try { - return jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject().get("securePath") - .getAsString(); - } catch (Exception e) { - return ""; - } - - } - - public int getWsPort() { - try { - return jsap.get("sparql11seprotocol").getAsJsonObject().get("availableProtocols").getAsJsonObject() - .get("ws").getAsJsonObject().get("port").getAsInt(); - } catch (Exception e) { - return -1; - } - } - public String getSubscribePath() { try { - switch (jsap.get("sparql11seprotocol").getAsJsonObject().get("protocol").getAsString()) { - - case "ws": - return jsap.get("sparql11seprotocol").getAsJsonObject().get("availableProtocols").getAsJsonObject() - .get("ws").getAsJsonObject().get("path").getAsString(); - case "wss": - return jsap.get("sparql11seprotocol").getAsJsonObject().get("availableProtocols").getAsJsonObject() - .get("wss").getAsJsonObject().get("path").getAsString(); - } + return jsap.get("sparql11seprotocol").getAsJsonObject().get("availableProtocols").getAsJsonObject() + .get(jsap.get("sparql11seprotocol").getAsJsonObject().get("protocol").getAsString()).getAsJsonObject().get("path").getAsString(); + } catch (Exception e) { + logger.error(e.getMessage()); return null; } - return null; } - public int getWssPort() { + public int getSubscribePort() { try { return jsap.get("sparql11seprotocol").getAsJsonObject().get("availableProtocols").getAsJsonObject() - .get("wss").getAsJsonObject().get("port").getAsInt(); + .get(jsap.get("sparql11seprotocol").getAsJsonObject().get("protocol").getAsString()).getAsJsonObject().get("port").getAsInt(); + } catch (Exception e) { + logger.error(e.getMessage()); return -1; } } @@ -298,24 +272,24 @@ public int getHttpsPort() { return -1; } - public String getRegisterPath() { - try{ - return jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject().get("register").getAsString(); - } - catch(Exception e) { + public String getRegisterUrl() { + try { + return jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject().get("register") + .getAsString(); + } catch (Exception e) { return ""; } - + } - public String getTokenRequestPath() { - try{ - return jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject().get("tokenRequest").getAsString(); - } - catch(Exception e) { + public String getTokenRequestUrl() { + try { + return jsap.get("sparql11seprotocol").getAsJsonObject().get("security").getAsJsonObject() + .get("tokenRequest").getAsString(); + } catch (Exception e) { return ""; } - + } private String getSecurityEncryptedValue(String value) throws SEPASecurityException { @@ -424,9 +398,8 @@ public String getBasicAuthorization() throws SEPASecurityException { // authorization = Base64.getEncoder().encode((id + ":" + // secret).getBytes("UTF-8")).toString(); return new String(buf, "UTF-8"); - } - catch(Exception e) { - throw new SEPASecurityException(e); + } catch (Exception e) { + throw new SEPASecurityException(e); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java index c755041c..53808378 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java @@ -58,6 +58,7 @@ import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties.SubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.protocol.SSLSecurityManager; @@ -73,6 +74,7 @@ import it.unibo.arces.wot.sepa.commons.response.RegistrationResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; +import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; /** * This class implements the SPARQL 1.1 Secure event protocol with SPARQL 1.1 @@ -96,6 +98,44 @@ public SPARQL11SEProtocol(SPARQL11SEProperties properties) throws SEPAProtocolEx this.properties = properties; } + + public SPARQL11SEProtocol(ApplicationProfile appProfile, String id, boolean update) throws SEPAProtocolException, SEPASecurityException { + super(appProfile,id,update); + } + + public SPARQL11SEProtocol(ApplicationProfile appProfile, ISubscriptionHandler handler,String id) throws SEPAProtocolException, SEPASecurityException { + super(appProfile,id,false); + + if (handler == null) { + logger.fatal("Handler is null"); + throw new SEPAProtocolException(new IllegalArgumentException("Handler is null")); + } + + this.properties = appProfile; + + if (appProfile.getSubscribeProtocol(id).equals(SubscriptionProtocol.WS)) { + // WS + int port = appProfile.getSubscribePort(id); + if (port != -1) + wsClient = new SPARQL11SEWebsocket( + "ws://" + appProfile.getSubscribeHost(id) + ":" + port + appProfile.getSubscribePath(id), handler); + else + wsClient = new SPARQL11SEWebsocket("ws://" + appProfile.getSubscribeHost(id) + appProfile.getSubscribePath(id), + handler); + } + else if (appProfile.getSubscribeProtocol(id).equals(SubscriptionProtocol.WSS)) { + // WSS + int port = appProfile.getSubscribePort(id); + if (port != -1) + wssClient = new SPARQL11SESecureWebsocket("wss://" + appProfile.getSubscribeHost(id) + ":" + port + + appProfile.getSubscribePath(id), handler); + else + wssClient = new SPARQL11SESecureWebsocket( + "wss://" + appProfile.getSubscribeHost(id) + appProfile.getSubscribePath(id), + handler); + } + } + /** * Create a protocol instance to communicate with SEPA. In particular use this * method if you want to subscribe about changes in the semantic graph. @@ -121,7 +161,7 @@ public SPARQL11SEProtocol(SPARQL11SEProperties properties, ISubscriptionHandler if (properties.getSubscriptionProtocol().equals(SubscriptionProtocol.WS)) { // WS - int port = properties.getWsPort(); + int port = properties.getSubscribePort(); if (port != -1) wsClient = new SPARQL11SEWebsocket( "ws://" + properties.getHost() + ":" + port + properties.getSubscribePath(), handler); @@ -131,13 +171,13 @@ public SPARQL11SEProtocol(SPARQL11SEProperties properties, ISubscriptionHandler } else if (properties.getSubscriptionProtocol().equals(SubscriptionProtocol.WSS)) { // WSS - int port = properties.getWssPort(); + int port = properties.getSubscribePort(); if (port != -1) wssClient = new SPARQL11SESecureWebsocket("wss://" + properties.getHost() + ":" + port - + properties.getSecurePath() + properties.getSubscribePath(), handler); + + properties.getSubscribePath(), handler); else wssClient = new SPARQL11SESecureWebsocket( - "wss://" + properties.getHost() + properties.getSecurePath() + properties.getSubscribePath(), + "wss://" + properties.getHost() + properties.getSubscribePath(), handler); } } @@ -149,10 +189,19 @@ public String toString() { /** * {@inheritDoc} */ - public Response update(UpdateRequest request) { - return super.update(request, 0); + public Response update(UpdateRequest request,int timeout,HTTPMethod method) { + return super.update(request, timeout,method); + } + + public Response update(UpdateRequest request,int timeout) { + return super.update(request, timeout,HTTPMethod.POST); } + public Response update(UpdateRequest request) { + //TODO: read default timeout from jsap + return super.update(request, 5000,HTTPMethod.POST); + } + /** * {@inheritDoc} */ @@ -248,7 +297,7 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op) { protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object request) { // Create the HTTPS request - URI uri; + URI uri = null; String path = null; int port = 0; @@ -300,8 +349,11 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req switch (op) { case REGISTER: - path = properties.getRegisterPath(); - port = properties.getHttpsPort(); + try { + uri = new URI(properties.getRegisterUrl()); + } catch (URISyntaxException e1) { + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); + } accept = "application/json"; contentType = "application/json"; @@ -314,6 +366,12 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req } break; case REQUESTTOKEN: + try { + uri = new URI(properties.getTokenRequestUrl()); + } catch (URISyntaxException e1) { + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); + } + String basic; try { basic = properties.getBasicAuthorization(); @@ -323,15 +381,13 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req if (basic == null) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Basic authorization in null. Register first"); - path = properties.getTokenRequestPath(); - port = properties.getHttpsPort(); authorization = "Basic " + basic; contentType = "application/json"; accept = "application/json"; break; case SECUREUPDATE: - path = properties.getSecurePath() + properties.getUpdatePath(); + path = properties.getUpdatePath(); port = properties.getHttpsPort(); accept = "text/plain"; @@ -352,7 +408,7 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req body.setContentType(contentType); break; case SECUREQUERY: - path = properties.getSecurePath() + properties.getQueryPath(); + path = properties.getQueryPath(); port = properties.getHttpsPort(); accept = "application/sparql-results+json"; @@ -375,7 +431,7 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req // POST request try { - uri = new URI("https", null, properties.getHost(), port, path, null, null); + if (uri == null) uri = new URI("https", null, properties.getHost(), port, path, null, null); } catch (URISyntaxException e1) { return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); } diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java similarity index 78% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java index 71263544..750b5ef0 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java @@ -19,6 +19,7 @@ package it.unibo.arces.wot.sepa.commons.protocol; import java.io.*; +import java.util.Base64; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -55,6 +56,12 @@ "path": "/update", "method": "POST | URL_ENCODED_POST", "format": "JSON | HTML" + }, + "authentication": { + "basic": { + "user": "admin", + "pass": "admin" + } } }, "graphs": { (optional) @@ -70,7 +77,7 @@ public class SPARQL11Properties { /** The Constant logger. */ - private static final Logger logger = LogManager.getLogger("SPARQL11ProtocolProperties"); + private static final Logger logger = LogManager.getLogger(); /** * The Enum SPARQLPrimitive (QUERY, UPDATE). @@ -137,7 +144,6 @@ private void loadProperties(File jsapFile) throws SEPAPropertiesException { try (final FileReader in = new FileReader(jsapFile)) { jsap = new JsonParser().parse(in).getAsJsonObject(); - // Validate the JSON elements validate(); this.propertiesFile = jsapFile; @@ -154,9 +160,6 @@ private void loadProperties(File jsapFile) throws SEPAPropertiesException { } logger.warn("USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker"); - - //throw new SEPAPropertiesException(new Exception( - // "USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker")); } } @@ -166,9 +169,9 @@ public String toString() { /** *
-{
- 	"host" : "localhost" ,
- 	"sparql11protocol": {
+	{
+	"host" : "localhost" ,
+	"sparql11protocol": {
 		"protocol": "http",
 		"port": 8000,
 		"query": {
@@ -182,7 +185,7 @@ public String toString() {
 			"format": "JSON | HTML"
 		}
 	}
-}
+	}
 	 * 
*/ protected void defaults() { @@ -204,13 +207,13 @@ protected void defaults() { update.add("format", new JsonPrimitive("JSON")); sparql11protocol.add("update", update); -// JsonObject graphs = new JsonObject(); -// graphs.add("default-graph-uri", new JsonPrimitive("http://default")); -// graphs.add("named-graph-uri", new JsonPrimitive("http://default")); -// graphs.add("using-graph-uri", new JsonPrimitive("http://default")); -// graphs.add("using-named-graph-uri", new JsonPrimitive("http://default")); -// jsap.add("graphs", graphs); - + // JsonObject graphs = new JsonObject(); + // graphs.add("default-graph-uri", new JsonPrimitive("http://default")); + // graphs.add("named-graph-uri", new JsonPrimitive("http://default")); + // graphs.add("using-graph-uri", new JsonPrimitive("http://default")); + // graphs.add("using-named-graph-uri", new JsonPrimitive("http://default")); + // jsap.add("graphs", graphs); + jsap.add("sparql11protocol", sparql11protocol); } @@ -218,6 +221,8 @@ protected void validate() throws SEPAPropertiesException { try { jsap.get("host").getAsString(); + jsap.get("sparql11protocol").getAsJsonObject().get("protocol").getAsString(); + jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("path").getAsString(); jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("method").getAsString(); jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("format").getAsString(); @@ -258,7 +263,8 @@ protected void storeProperties(String propertiesFile) throws SEPAPropertiesExcep * @return the host (default is localhost) */ public String getHost() { - if (jsap.get("sparql11protocol").getAsJsonObject().get("host") != null) return jsap.get("sparql11protocol").getAsJsonObject().get("host").getAsString(); + if (jsap.get("sparql11protocol").getAsJsonObject().get("host") != null) + return jsap.get("sparql11protocol").getAsJsonObject().get("host").getAsString(); return jsap.get("host").getAsString(); } @@ -278,12 +284,10 @@ public int getHttpPort() { /** * Gets the default graph URI. * - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } + * "graphs": { "default-graph-uri": "http://default", "named-graph-uri": + * "http://default", "using-graph-uri": "http://default", + * "using-named-graph-uri": "http://default" } + * * @return the default graph URI */ public String getDefaultGraphURI() { @@ -297,12 +301,10 @@ public String getDefaultGraphURI() { /** * Gets the named graph URI. * - "graphs": { - "default-graph-uri ": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } + * "graphs": { "default-graph-uri ": "http://default", "named-graph-uri": + * "http://default", "using-graph-uri": "http://default", + * "using-named-graph-uri": "http://default" } + * * @return the default graph URI */ public String getNamedGraphURI() { @@ -316,12 +318,10 @@ public String getNamedGraphURI() { /** * Gets the using graph URI. * - "graphs": { - "default-graph-uri ": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } + * "graphs": { "default-graph-uri ": "http://default", "named-graph-uri": + * "http://default", "using-graph-uri": "http://default", + * "using-named-graph-uri": "http://default" } + * * @return the default graph URI */ public String getUsingGraphURI() { @@ -335,12 +335,10 @@ public String getUsingGraphURI() { /** * Gets the using named graph URI. * - "graphs": { - "default-graph-uri ": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } + * "graphs": { "default-graph-uri ": "http://default", "named-graph-uri": + * "http://default", "using-graph-uri": "http://default", + * "using-named-graph-uri": "http://default" } + * * @return the default graph URI */ public String getUsingNamedGraphURI() { @@ -368,7 +366,8 @@ public String getUpdatePath() { * @see HTTPMethod */ public HTTPMethod getUpdateMethod() { - switch (jsap.get("sparql11protocol").getAsJsonObject().get("update").getAsJsonObject().get("method").getAsString()) { + switch (jsap.get("sparql11protocol").getAsJsonObject().get("update").getAsJsonObject().get("method") + .getAsString()) { case "POST": return HTTPMethod.POST; case "URL_ENCODED_POST": @@ -384,7 +383,8 @@ public HTTPMethod getUpdateMethod() { * @return the update HTTP Accept header string */ public String getUpdateAcceptHeader() { - switch (jsap.get("sparql11protocol").getAsJsonObject().get("update").getAsJsonObject().get("format").getAsString()) { + switch (jsap.get("sparql11protocol").getAsJsonObject().get("update").getAsJsonObject().get("format") + .getAsString()) { case "JSON": return "application/json"; case "HTML": @@ -411,7 +411,8 @@ public String getQueryPath() { * @see HTTPMethod */ public HTTPMethod getQueryMethod() { - switch (jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("method").getAsString()) { + switch (jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("method") + .getAsString()) { case "POST": return HTTPMethod.POST; case "GET": @@ -430,7 +431,8 @@ public HTTPMethod getQueryMethod() { * */ public String getQueryAcceptHeader() { - switch (jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("format").getAsString()) { + switch (jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("format") + .getAsString()) { case "JSON": return "application/sparql-results+json"; case "XML": @@ -443,7 +445,8 @@ public String getQueryAcceptHeader() { } public String getUpdateContentTypeHeader() { - switch (jsap.get("sparql11protocol").getAsJsonObject().get("update").getAsJsonObject().get("method").getAsString()) { + switch (jsap.get("sparql11protocol").getAsJsonObject().get("update").getAsJsonObject().get("method") + .getAsString()) { case "POST": return "application/sparql-update"; case "URL_ENCODED_POST": @@ -454,7 +457,8 @@ public String getUpdateContentTypeHeader() { } public String getQueryContentTypeHeader() { - switch (jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("method").getAsString()) { + switch (jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("method") + .getAsString()) { case "POST": return "application/sparql-query"; case "URL_ENCODED_POST": @@ -463,4 +467,33 @@ public String getQueryContentTypeHeader() { return "application/sparql-query"; } } + + public String getProtocolScheme() { + return jsap.get("sparql11protocol").getAsJsonObject().get("protocol").getAsString(); + } + + public boolean isAuthenticationRequired() { + try { + return jsap.get("sparql11protocol").getAsJsonObject().has("authentication"); + } + catch(Exception e) { + return false; + } + } + + public String getAuthorizationHeader() { + try { + if(jsap.get("sparql11protocol").getAsJsonObject().get("authentication").getAsJsonObject().has("basic")) { + String user = jsap.get("sparql11protocol").getAsJsonObject().get("authentication").getAsJsonObject().get("basic").getAsJsonObject().get("user").getAsString(); + String pass = jsap.get("sparql11protocol").getAsJsonObject().get("authentication").getAsJsonObject().get("basic").getAsJsonObject().get("pass").getAsString(); + byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); + return "Basic " +new String(buf, "UTF-8"); + } + } + catch(Exception e) { + logger.warn(e.getMessage()); + return ""; + } + return ""; + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java new file mode 100644 index 00000000..b5439d57 --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -0,0 +1,574 @@ +/* This class implements the SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/) + * + * Author: Luca Roffia (luca.roffia@unibo.it) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package it.unibo.arces.wot.sepa.commons.protocol; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.http.Consts; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; +import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.QueryResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; +import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; +import it.unibo.arces.wot.sepa.timing.Timings; + +import org.apache.logging.log4j.Logger; + +import com.google.gson.JsonParser; + +import org.apache.logging.log4j.LogManager; + +/** + * This class implements the SPARQL 1.1 Protocol + */ + +public class SPARQL11Protocol implements java.io.Closeable { + + /** The log4j2 logger. */ + private static final Logger logger = LogManager.getLogger(); + + /** The Java bean name. */ + protected static String mBeanName = "arces.unibo.SEPA.server:type=SPARQL11Protocol"; + + /** The http client. */ + final CloseableHttpClient httpClient = HttpClients.createDefault(); + + /** The url components. */ + private String scheme = "http"; + private String host = "localhost"; + private int port = -1; + private String updatePath = "/update"; + private String queryPath = "/query"; + + /** Endpoint authentication */ + private boolean authentication = false; + private String authorizationHeader = null; + + public SPARQL11Protocol(SPARQL11Properties properties) throws SEPAProtocolException { + if (properties == null) { + logger.fatal("Properties are null"); + throw new SEPAProtocolException(new IllegalArgumentException("Properties are null")); + } + + this.scheme = properties.getProtocolScheme(); + this.host = properties.getHost(); + this.port = properties.getHttpPort(); + this.updatePath = properties.getUpdatePath(); + this.queryPath = properties.getQueryPath(); + + this.authentication = properties.isAuthenticationRequired(); + this.authorizationHeader = properties.getAuthorizationHeader(); + } + + public SPARQL11Protocol(ApplicationProfile appProfile, String id, boolean update) throws SEPAProtocolException { + if (scheme == null | host == null | updatePath == null | queryPath == null) { + logger.fatal("Properties are null"); + throw new SEPAProtocolException(new IllegalArgumentException("Properties are null")); + } + if (update) { + this.scheme = appProfile.getUpdateProtocol(id); + this.host = appProfile.getUpdateHost(id); + this.port = appProfile.getUpdatePort(id); + this.updatePath = appProfile.getUpdatePath(id); + this.queryPath = appProfile.getQueryPath(); + + this.authentication = appProfile.isAuthenticationRequiredForUpdate(id); + this.authorizationHeader = appProfile.getUpdateAuthorizationHeader(id); + } else { + this.scheme = appProfile.getQueryProtocol(id); + this.host = appProfile.getQueryHost(id); + this.port = appProfile.getQueryPort(id); + this.updatePath = appProfile.getUpdatePath(); + this.queryPath = appProfile.getQueryPath(id); + + this.authentication = appProfile.isAuthenticationRequiredForQuery(id); + this.authorizationHeader = appProfile.getQueryAuthorizationHeader(id); + } + } + + private Response executeRequest(HttpUriRequest req, int timeout, boolean update, int token) { + CloseableHttpResponse httpResponse = null; + HttpEntity responseEntity = null; + int responseCode = 0; + String responseBody = null; + + try { + // Add "Authorization" header if required + if (authentication) { + req.setHeader("Authorization", authorizationHeader); + } + + // Execute HTTP request + logger.debug("Execute HTTP request (timeout: " + timeout + " ms) " + req.toString(), timeout); + long start = Timings.getTime(); + httpResponse = httpClient.execute(req); + long stop = Timings.getTime(); + if (update) + Timings.log("ENDPOINT_UPDATE_TIME", start, stop); + else + Timings.log("ENDPOINT_QUERY_TIME", start, stop); + + // Status code + responseCode = httpResponse.getStatusLine().getStatusCode(); + + // Body + responseEntity = httpResponse.getEntity(); + responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); + logger.debug(String.format("Response (%d): %s", responseCode, responseBody)); + EntityUtils.consume(responseEntity); + } catch (IOException e) { + return new ErrorResponse(token, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } finally { + try { + if (httpResponse != null) + httpResponse.close(); + } catch (IOException e) { + return new ErrorResponse(token, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + responseEntity = null; + } + if (responseCode >= 400) { + return new ErrorResponse(token, responseCode, responseBody); +// try { +//// return new ErrorResponse(token, new JsonParser().parse(responseBody).getAsJsonObject()); +//// } catch (Exception e) { +//// return new ErrorResponse(token, responseCode, responseBody); +//// } + } + + if (update) + return new UpdateResponse(token, responseBody); + try { + return new QueryResponse(token, new JsonParser().parse(responseBody).getAsJsonObject()); + } catch (Exception e) { + return new ErrorResponse(token, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + + /** + * Implements a SPARQL 1.1 update operation + * (https://www.w3.org/TR/sparql11-protocol/) + * + *
+	 * update via URL-encoded POST 
+	 * - Method: POST
+	 * - Query Parameters: None
+	 * - Content Type: application/x-www-form-urlencoded
+	 * - Body: URL-encoded, ampersand-separated query parameters. 
+	 * 	update (exactly 1). 
+	 * 	using-graph-uri (0 or more). 
+	 * 	using-named-graph-uri (0 or more)
+	 * 
+	 * update via POST directly
+	 * - Method: POST
+	 * - Query Parameters: 
+	 * 	using-graph-uri (0 or more); 
+	 * 	using-named-graph-uri (0 or more)
+	 * - Content Type: application/sparql-update
+	 * - Body: Unencoded SPARQL update request string
+	 * 
+ * + * 2.2.3 Specifying an RDF Dataset + * + *
+	 * SPARQL Update requests are executed against a Graph Store, a mutable
+	 * container of RDF graphs managed by a SPARQL service. The WHERE clause of a
+	 * SPARQL update DELETE/INSERT operation [UPDATE] matches against data in an RDF
+	 * Dataset, which is a subset of the Graph Store. The RDF Dataset for an update
+	 * operation may be specified either in the operation string itself using the
+	 * USING, USING NAMED, and/or WITH keywords, or it may be specified via the
+	 * using-graph-uri and using-named-graph-uri parameters.
+	 * 
+	 * It is an error to supply the using-graph-uri or using-named-graph-uri
+	 * parameters when using this protocol to convey a SPARQL 1.1 Update request
+	 * that contains an operation that uses the USING, USING NAMED, or WITH clause.
+	 * 
+	 * A SPARQL Update processor should treat each occurrence of the
+	 * using-graph-uri=g parameter in an update protocol operation as if a USING 
+	 * clause were included for every operation in the SPARQL 1.1 Update request.
+	 * Similarly, a SPARQL Update processor should treat each occurrence of the
+	 * using-named-graph-uri=g parameter in an update protocol operation as if a
+	 * USING NAMED  clause were included for every operation in the SPARQL 1.1
+	 * Update request.
+	 * 
+	 * UPDATE 2.2 update operation The response to an update request indicates
+	 * success or failure of the request via HTTP response status code.
+	 * 
+ */ + // public Response update(UpdateRequest req, int timeout) { + // return post(req, timeout, false); + // } + + public Response update(UpdateRequest req, int timeout, HTTPMethod method) { + switch (method) { + case GET: + // *********************** + // OpenLink VIRTUOSO PATCH (not supported by SPARQL 1.1 Protocol) + // *********************** + return patchVirtuoso(req, timeout); + case POST: + return post(req, timeout, false); + case URL_ENCODED_POST: + return post(req, timeout, true); + } + + return post(req, timeout, false); + } + + private Response post(UpdateRequest req, int timeout, boolean urlEncoded) { + StringEntity requestEntity = null; + HttpPost post; + String graphs = ""; + + try { + if (req.getUsingGraphUri() != null) { + + graphs += "using-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); + + if (req.getUsingNamedGraphUri() != null) { + graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } + } else if (req.getUsingNamedGraphUri() != null) { + graphs += "using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + try { + if (!urlEncoded) + post = new HttpPost(new URI(scheme, null, host, port, updatePath, graphs, null)); + else + post = new HttpPost(new URI(scheme, null, host, port, updatePath, null, null)); + } catch (URISyntaxException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + post.setHeader("Accept", req.getAcceptHeader()); + if (!urlEncoded) + post.setHeader("Content-Type", "application/sparql-update"); + else + post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); + post.setEntity(requestEntity); + + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) + .build(); + post.setConfig(requestConfig); + + return executeRequest(post, timeout, true, req.getToken()); + } + + private Response patchVirtuoso(UpdateRequest req, int timeout) { + // 1) "INSERT DATA" is not supported. Only INSERT (also if the WHERE is not + // present). + String fixedSparql = req.getSPARQL(); + Pattern p = null; + try { + p = Pattern.compile( + "(?.*)(delete)([^{]*)(?.*)(insert)([^{]*)(?.*)|(?.*)(delete)([^{]*)(?.*)|(?.*)(insert)([^{]*)(?.*)", + Pattern.CASE_INSENSITIVE); + + Matcher m = p.matcher(req.getSPARQL()); + if (m.matches()) { + if (m.group("update") != null) { + fixedSparql = m.group("update") + " delete " + m.group("udtriples") + " insert " + + m.group("uitriples"); + } else if (m.group("insert") != null) { + fixedSparql = m.group("insert") + " insert " + m.group("itriples"); + } else { + fixedSparql = m.group("delete") + " delete " + m.group("dtriples"); + } + } + } catch (Exception e) { + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + // 2) SPARQL 1.1 Update are issued as GET request (like for a SPARQL 1.1 Query) + String query; + try { + // custom "format" parameter + query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format=" + + URLEncoder.encode(req.getAcceptHeader(), "UTF-8"); + } catch (UnsupportedEncodingException e1) { + logger.error(e1.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); + } + + // 3) Named-graphs specified like a query + String graphs = ""; + try { + if (req.getUsingGraphUri() != null) { + + graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); + + if (req.getUsingNamedGraphUri() != null) { + graphs += "&named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } + } else if (req.getUsingNamedGraphUri() != null) { + graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + if (!graphs.equals("")) + query += "&" + graphs; + + String url; + if (port != -1) + url = "http://" + host + ":" + port + queryPath + "?" + query; + else + url = "http://" + host + queryPath + "?" + query; + + HttpGet get; + get = new HttpGet(url); + + get.setHeader("Accept", req.getAcceptHeader()); + + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) + .build(); + get.setConfig(requestConfig); + + return executeRequest(get, timeout, false, req.getToken()); + } + + /** + * Implements a SPARQL 1.1 query operation + * (https://www.w3.org/TR/sparql11-protocol/) + * + *
+	 * query via GET
+	 * - HTTP Method: GET
+	 * - Query String Parameters: query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
+	 * - Request Content Type: None
+	 * - Request Message Body: None
+	 * 
+	 * query via URL-encoded POST 
+	 * - HTTP Method: POST
+	 * - Query String Parameters: None
+	 * - Request Content Type: application/x-www-form-urlencoded
+	 * - Request Message Body: URL-encoded, ampersand-separated query parameters. query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
+	 * 
+	 * query via POST directly
+	 * - HTTP Method: POST
+	 * - Query String parameters: default-graph-uri (0 or more). named-graph-uri (0 or more)
+	 * - Request Content Type: application/sparql-query
+	 * - Request Message Body: Unencoded SPARQL update request string
+	 *
+	 * 2.1.4 Specifying an RDF Dataset
+	 * 
+	 * A SPARQL query is executed against an RDF Dataset. The RDF Dataset for a query may be specified either 
+	 * via the default-graph-uri and named-graph-uri parameters in the SPARQL Protocol or in the SPARQL query 
+	 * string using the FROM and FROM NAMED keywords. If different RDF Datasets are specified in both the protocol 
+	 * request and the SPARQL query string, then the SPARQL service must execute the query using the RDF Dataset 
+	 * given in the protocol request.
+	 * 
+	 * Note that a service may reject a query with HTTP response code 400 if the service does not allow protocol 
+	 * clients to specify the RDF Dataset. If an RDF Dataset is not specified in either the protocol request or 
+	 * the SPARQL query string, then implementations may execute the query against an implementation-defined default RDF dataset.
+	 * 
+	 * QUERY 2.1.5 Accepted Response Formats
+	 * 
+	 * Protocol clients should use HTTP content negotiation [RFC2616] to request
+	 * response formats that the client can consume. See below for more on
+	 * potential response formats.
+	 * 
+	 * 2.1.6 Success Responses
+	 * 
+	 * The SPARQL Protocol uses the response status codes defined in HTTP to
+	 * indicate the success or failure of an operation. Consult the HTTP
+	 * specification [RFC2616] for detailed definitions of each status code.
+	 * While a protocol service should use a 2XX HTTP response code for a
+	 * successful query, it may choose instead to use a 3XX response code as per
+	 * HTTP.
+	 * 
+	 * The response body of a successful query operation with a 2XX response is
+	 * either:
+	 * 
+	 * a SPARQL Results Document in XML, JSON, or CSV/TSV format (for SPARQL
+	 * Query forms SELECT and ASK); or, an RDF graph [RDF-CONCEPTS] serialized,
+	 * for example, in the RDF/XML syntax [RDF-XML], or an equivalent RDF graph
+	 * serialization, for SPARQL Query forms DESCRIBE and CONSTRUCT). The
+	 * content type of the response to a successful query operation must be the
+	 * media type defined for the format of the response body.
+	 * 
+	 * 2.1.7 Failure Responses
+	 * 
+	 * The HTTP response codes applicable to an unsuccessful query operation
+	 * include:
+	 * 
+	 * 400 if the SPARQL query supplied in the request is not a legal sequence
+	 * of characters in the language defined by the SPARQL grammar; or, 500 if
+	 * the service fails to execute the query. SPARQL Protocol services may also
+	 * return a 500 response code if they refuse to execute a query. This
+	 * response does not indicate whether the server may or may not process a
+	 * subsequent, identical request or requests. The response body of a failed
+	 * query request is implementation defined. Implementations may use HTTP
+	 * content negotiation to provide human-readable or machine-processable (or
+	 * both) information about the failed query request.
+	 * 
+	 * A protocol service may use other 4XX or 5XX HTTP response codes for other
+	 * failure conditions, as per HTTP.
+	 *
+	 * 
+ */ + public Response query(QueryRequest req, int timeout) { + return post(req, timeout, false); + } + + public Response query(QueryRequest req, int timeout, HTTPMethod method) { + switch (method) { + case GET: + return get(req, timeout); + case POST: + return post(req, timeout, false); + case URL_ENCODED_POST: + return post(req, timeout, true); + } + return post(req, timeout, false); + } + + private Response post(QueryRequest req, int timeout, boolean urlEncoded) { + StringEntity requestEntity = null; + HttpPost post; + String graphs = ""; + + try { + if (req.getDefaultGraphUri() != null) { + + graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); + + if (req.getNamedGraphUri() != null) { + graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + } + } else if (req.getNamedGraphUri() != null) { + graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + try { + if (!urlEncoded) + post = new HttpPost(new URI(scheme, null, host, port, queryPath, graphs, null)); + else + post = new HttpPost(new URI(scheme, null, host, port, queryPath, null, null)); + } catch (URISyntaxException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + post.setHeader("Accept", req.getAcceptHeader()); + if (!urlEncoded) + post.setHeader("Content-Type", "application/sparql-query"); + else + post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); + post.setEntity(requestEntity); + + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) + .build(); + post.setConfig(requestConfig); + + return executeRequest(post, timeout, false, req.getToken()); + } + + private Response get(QueryRequest req, int timeout) { + String query; + try { + query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"); + } catch (UnsupportedEncodingException e1) { + logger.error(e1.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); + } + + String graphs = ""; + try { + if (req.getDefaultGraphUri() != null) { + + graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); + + if (req.getNamedGraphUri() != null) { + graphs += "&named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + } + } else if (req.getNamedGraphUri() != null) { + graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + if (!graphs.equals("")) + query += "&" + graphs; + + String url; + if (port != -1) + url = "http://" + host + ":" + port + queryPath + "?" + query; + else + url = "http://" + host + queryPath + "?" + query; + + HttpGet get; + get = new HttpGet(url); + + get.setHeader("Accept", req.getAcceptHeader()); + + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) + .build(); + get.setConfig(requestConfig); + + return executeRequest(get, timeout, false, req.getToken()); + } + + @Override + public void close() throws IOException { + httpClient.close(); + } +} diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SSLSecurityManager.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SSLSecurityManager.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SSLSecurityManager.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SSLSecurityManager.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java similarity index 77% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index 04f6f28d..b33131d5 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -26,7 +26,9 @@ * */ public class QueryRequest extends Request { - + private String default_graph_uri = null; + private String named_graph_uri = null; + /** * Instantiates a new query request. * @@ -55,4 +57,24 @@ public String toString() { } + public String getAcceptHeader() { + return "application/sparql-results+json"; + } + + public String getDefaultGraphUri() { + return default_graph_uri; + } + + public void setDefaultGraphUri(String graphUri) { + this.default_graph_uri = graphUri; + } + + public String getNamedGraphUri() { + return named_graph_uri; + } + + public void setNamedGraphUri(String graphUri) { + this.named_graph_uri = graphUri; + } + } diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java similarity index 67% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java index 54112bda..7556c6aa 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java @@ -17,6 +17,9 @@ */ package it.unibo.arces.wot.sepa.commons.request; +import java.io.UnsupportedEncodingException; +import java.util.Base64; + // TODO: Auto-generated Javadoc /** * This class represents a generic request (i.e., QUERY, UPDATE, SUBSCRIBE, @@ -31,6 +34,20 @@ public abstract class Request { /** The sparql. */ protected String sparql; + /** + * Authorization related members + * + * 1) The 'Basic' HTTP Authentication Scheme, + * https://tools.ietf.org/html/rfc7617 + */ + + private enum AUTHENTICATION_SCHEMA { + DISABLED, BASIC + }; + + private AUTHENTICATION_SCHEMA authorization = AUTHENTICATION_SCHEMA.DISABLED; + private String basicAuthorizationHeader; + /** * Instantiates a new request. * @@ -54,7 +71,7 @@ public Request(String sparql) { this.token = -1; this.sparql = sparql; } - + public void setToken(int token) { this.token = token; } @@ -76,20 +93,46 @@ public int getToken() { public String getSPARQL() { return sparql; } - + public boolean isQueryRequest() { return this.getClass().equals(QueryRequest.class); } - + public boolean isUpdateRequest() { return this.getClass().equals(UpdateRequest.class); } - + public boolean isSubscribeRequest() { return this.getClass().equals(SubscribeRequest.class); } - + public boolean isUnsubscribeRequest() { return this.getClass().equals(UnsubscribeRequest.class); } + + public boolean setBasicAuthentication(String user, String pass) { + authorization = AUTHENTICATION_SCHEMA.BASIC; + + try { + byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); + basicAuthorizationHeader = "Basic " +new String(buf, "UTF-8"); + } catch (UnsupportedEncodingException e) { + return false; + } + return true; + } + + public String getAuthorizationHeader() { + switch (authorization) { + case BASIC: + return basicAuthorizationHeader; + default: + return ""; + } + } + + public boolean isAuthenticationRequired() { + return authorization != AUTHENTICATION_SCHEMA.DISABLED; + } + } diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java similarity index 54% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java index 56e61150..dfb7d090 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java @@ -26,7 +26,13 @@ * */ public class UpdateRequest extends Request { - + /* It is an error to supply the using-graph-uri or using-named-graph-uri + * parameters when using this protocol to convey a SPARQL 1.1 Update request + * that contains an operation that uses the USING, USING NAMED, or WITH clause. + */ + private String using_graph_uri = null; + private String named_graph_uri = null; + /** * Instantiates a new update request. * @@ -53,4 +59,33 @@ public String toString() { if (token != -1) return "UPDATE #"+token+" "+sparql; return "UPDATE "+sparql; } + + /* SPARQL Update requests are executed against a Graph Store, a mutable + * container of RDF graphs managed by a SPARQL service. The WHERE clause of a + * SPARQL update DELETE/INSERT operation [UPDATE] matches against data in an RDF + * Dataset, which is a subset of the Graph Store. The RDF Dataset for an update + * operation may be specified either in the operation string itself using the + * USING, USING NAMED, and/or WITH keywords, or it may be specified via the + * using-graph-uri and using-named-graph-uri parameters. + */ + + public String getUsingGraphUri() { + return using_graph_uri; + } + + public void setUsingGraphUri(String using_graph_uri) { + this.using_graph_uri = using_graph_uri; + } + + public String getUsingNamedGraphUri() { + return named_graph_uri; + } + + public void setNamedGraphUri(String named_graph_uri) { + this.named_graph_uri = named_graph_uri; + } + + public String getAcceptHeader() { + return "application/json"; + } } diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Ping.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Ping.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Ping.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Ping.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/RegistrationResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/RegistrationResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/RegistrationResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/RegistrationResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/ARBindingsResults.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/ARBindingsResults.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/ARBindingsResults.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/ARBindingsResults.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/BindingsResults.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermBNode.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermBNode.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermBNode.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermBNode.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java similarity index 100% rename from commons/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java similarity index 87% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java index 6fcde9ef..2ad002da 100644 --- a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java @@ -42,17 +42,20 @@ public Aggregator(ApplicationProfile appProfile,String subscribeID,String update throw new SEPAProtocolException(new IllegalArgumentException("Update ID is null null")); } - if (appProfile.update(updateID) == null) { + if (appProfile.getSPARQLUpdate(updateID) == null) { logger.fatal("UPDATE ID " +updateID+" not found in "+appProfile.getFileName()); throw new IllegalArgumentException("UPDATE ID " +updateID+" not found in "+appProfile.getFileName()); } SPARQL_ID = updateID; - sparqlUpdate = appProfile.update(updateID); + sparqlUpdate = appProfile.getSPARQLUpdate(updateID); } - public final Response update(Bindings forcedBindings){ + public final Response update(Bindings forcedBindings){ + return update(forcedBindings,5000); + } + public final Response update(Bindings forcedBindings,int timeout){ if (protocolClient == null || sparqlUpdate == null) { logger.fatal("Aggregator not initialized"); return new ErrorResponse(-1,400,"Aggregator not initialized"); @@ -62,6 +65,6 @@ public final Response update(Bindings forcedBindings){ logger.debug(" "+ SPARQL_ID+" ==> "+sparql); - return protocolClient.update(new UpdateRequest(sparql)); + return protocolClient.update(new UpdateRequest(sparql),timeout); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java new file mode 100644 index 00000000..d7dd11e0 --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java @@ -0,0 +1,707 @@ +/* This class implements a JSON parser of an JSAP (JSON Application Profile) file + * + * Author: Luca Roffia (luca.roffia@unibo.it) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with this program. If not, see . +*/ + +package it.unibo.arces.wot.sepa.pattern; + +import java.util.Base64; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.sparql.Bindings; +import it.unibo.arces.wot.sepa.commons.sparql.RDFTerm; +import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; +import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; + +/** + * JSAP file example + * + *
+ * {
+ * "host" : "localhost" ,
+ 	"sparql11protocol": {
+ 		"host":"override default host", 	(optional)
+		"protocol": "http",
+		"port": 8000,					(optional)
+		"query": {
+			"path": "/query",
+			"method": "GET | POST | URL_ENCODED_POST",
+			"format": "JSON | XML | CSV"
+		},
+		"update": {
+			"path": "/update",
+			"method": "POST | URL_ENCODED_POST",
+			"format": "JSON | HTML"
+		}
+	},
+  	"sparql11seprotocol": {
+  	    "host":"override default host", 	(optional)
+		"protocol": "ws",
+		"availableProtocols": {
+			"ws": {
+			    "host":"override default host", 	(optional)
+				"port": 9000,
+				"path": "/subscribe"
+			},
+			"wss": {
+			     "host":"override default host", 	(optional)
+				"port": 9443,
+				"path": "/secure/subscribe"
+			}
+		},
+		"security": {
+			"register": "https://localhost:8443/oauth/register",
+			"tokenRequest": "https://localhost:8443/oauth/token",
+			"client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs",
+			"client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg",
+			"jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT",
+			"expires": "04/5tRBT5n/VJ0XQASgs/w==",
+			"type": "XPrHEX2xHy+5IuXHPHigMw=="
+		},
+	"graphs": {
+		"default-graph-uri ": "http://default",
+		"named-graph-uri": "http://default",
+		"using-graph-uri": "http://default",
+		"using-named-graph-uri": "http://default"
+	},	
+	"extended" :{},
+	"namespaces" : {
+			"chat" : "http://wot.arces.unibo.it/chat#" ,
+			"rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#"},
+	"updates" : {
+			"UPDATE_1" : {
+				"sparql" : "..." ,
+				"forcedBindings" : {
+					"variable_1" : {
+						"type" : "literal" ,
+						"value" : ""}
+					 ,
+					"variable_2" : {
+						"type" : "literal" ,
+						"value" : ""}
+					 ,
+					"variable_N" : {
+						"type" : "uri" ,
+						"value" : ""}
+				},
+				"sparql11protocol" :{...} (optional)
+			}
+			 ,
+			"UPDATE_N" : {
+				"sparql" : "..."
+			}
+		}
+		 ,
+		"queries" : {
+			"QUERY_1" : {
+				"sparql" : "..." ,
+				"forcedBindings" : {
+					"variable_1" : {
+						"type" : "literal" ,
+						"value" : ""}
+					 ,
+					"variable_2" : {
+						"type" : "literal" ,
+						"value" : ""}
+					 ,
+					"variable_N" : {
+						"type" : "uri" ,
+						"value" : ""}
+				},
+				"sparql11protocol" :{...} (optional),
+				"sparql11seprotocol" :{...} (optional)
+			}
+			 ,
+			"QUERY_N" : {
+				"sparql" : "..."
+			}
+		}
+		}}
+ * 
+ */ +public class ApplicationProfile extends SPARQL11SEProperties { + public ApplicationProfile(String propertiesFile) throws SEPAPropertiesException { + super(propertiesFile); + } + + public ApplicationProfile(String propertiesFile, byte[] secret) throws SEPAPropertiesException { + super(propertiesFile, secret); + } + + protected Logger logger = LogManager.getLogger(); + + public JsonObject getExtendedData() { + try { + return jsap.get("extended").getAsJsonObject(); + } catch (Exception e) { + logger.error(e.getMessage()); + } + + return null; + } + + /* + * UPDATE + */ + + public boolean isAuthenticationRequiredForUpdate(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().has("authentication"); + } + catch(Exception e) { + logger.debug(e.getMessage()); + try{ + return jsap.has("authentication"); + } + catch (Exception e1) { + logger.debug(e1.getMessage()); + return false; + } + } + } + + public String getUpdateAuthorizationHeader(String id) { + try { + if (jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().has("authentication")) { + if (jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("authentication") + .getAsJsonObject().has("basic")) { + String user = jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("authentication") + .getAsJsonObject().get("basic").getAsJsonObject().get("user").getAsString(); + String pass = jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("authentication") + .getAsJsonObject().get("basic").getAsJsonObject().get("pass").getAsString(); + byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); + return "Basic " + new String(buf, "UTF-8"); + } + } else if (jsap.has("authentication")) { + if (jsap.get("authentication").getAsJsonObject().has("basic")) { + String user = jsap.get("authentication").getAsJsonObject().get("basic").getAsJsonObject() + .get("user").getAsString(); + String pass = jsap.get("authentication").getAsJsonObject().get("basic").getAsJsonObject() + .get("pass").getAsString(); + byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); + return "Basic " + new String(buf, "UTF-8"); + } + } + } catch (Exception e) { + logger.warn(e.getMessage()); + return ""; + } + return ""; + } + + public String getSPARQLUpdate(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + return null; + } + + public String getUpdateHost(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("host").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getHost(); + } + + public String getUpdateAcceptHeader(String id) { + try { + if (jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("format").getAsString().equals("JSON")) + return "application/json"; + else + return "application/html"; + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getUpdateAcceptHeader(); + } + + public HTTPMethod getUpdateMethod(String id) { + try { + if (jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("method").getAsString().equals("URL_ENCODED_POST")) + return HTTPMethod.URL_ENCODED_POST; + return HTTPMethod.POST; + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getUpdateMethod(); + } + + public String getUpdateProtocol(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("protocol").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getProtocolScheme(); + } + + public String getUpdatePath(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("path").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getUpdatePath(); + } + + public int getUpdatePort(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("port").getAsInt(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getHttpPort(); + } + + public String getUsingNamedGraphURI(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() + .get("using-named-graph-uri").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getUsingNamedGraphURI(); + } + + public String getUsingGraphURI(String id) { + try { + return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() + .get("using-graph-uri").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getUsingGraphURI(); + } + + /* + * QUERY + */ + public boolean isAuthenticationRequiredForQuery(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().has("authentication"); + } + catch(Exception e) { + logger.debug(e.getMessage()); + try{ + return jsap.has("authentication"); + } + catch (Exception e1) { + logger.debug(e1.getMessage()); + return false; + } + } + } + + public String getQueryAuthorizationHeader(String id) { + try { + if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().has("authentication")) { + if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("authentication") + .getAsJsonObject().has("basic")) { + String user = jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("authentication") + .getAsJsonObject().get("basic").getAsJsonObject().get("user").getAsString(); + String pass = jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("authentication") + .getAsJsonObject().get("basic").getAsJsonObject().get("pass").getAsString(); + byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); + return "Basic " + new String(buf, "UTF-8"); + } + } else if (jsap.has("authentication")) { + if (jsap.get("authentication").getAsJsonObject().has("basic")) { + String user = jsap.get("authentication").getAsJsonObject().get("basic").getAsJsonObject() + .get("user").getAsString(); + String pass = jsap.get("authentication").getAsJsonObject().get("basic").getAsJsonObject() + .get("pass").getAsString(); + byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); + return "Basic " + new String(buf, "UTF-8"); + } + } + } catch (Exception e) { + logger.warn(e.getMessage()); + return ""; + } + return ""; + } + + public String getSPARQLQuery(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + return null; + } + + public String getQueryHost(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("host").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getHost(); + } + + public String getQueryProtocol(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("protocol").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getProtocolScheme(); + } + + public int getQueryPort(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("port").getAsInt(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getHttpPort(); + } + + public String getQueryPath(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("path").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getQueryPath(); + } + + public HTTPMethod getQueryMethod(String id) { + try { + if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("method").getAsString().equals("URL_ENCODED_POST")) + return HTTPMethod.URL_ENCODED_POST; + else if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") + .getAsJsonObject().get("method").getAsString().equals("GET")) + return HTTPMethod.GET; + return HTTPMethod.POST; + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getUpdateMethod(); + } + + public String getQueryFormat(String id) { + try { + switch (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("format").getAsString()) { + case "JSON": + return "application/sparql-results+json"; + case "XML": + return "application/sparql-results+xml"; + case "CSV": + return "text/csv"; + default: + return "application/sparql-results+json"; + } + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getQueryAcceptHeader(); + } + + public String getNamedGraphURI(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() + .get("named-graph-uri").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getNamedGraphURI(); + } + + public String getDefaultGraphURI(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() + .get("default-graph-uri").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getDefaultGraphURI(); + } + + /* + * SUBSCRIBE + */ + + public String getSubscribeHost(String id) { + String protocol = null; + try { + protocol = jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("protocol").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("host").getAsString(); + } catch (Exception e1) { + logger.debug(e1.getMessage()); + + return super.getHost(); + } + } + + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("availableProtocols").getAsJsonObject().get(protocol).getAsJsonObject() + .get("host").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getHost(); + } + + public int getSubscribePort(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("availableProtocols").getAsJsonObject() + .get(jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("protocol").getAsString()) + .getAsJsonObject().get("port").getAsInt(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getSubscribePort(); + } + + public String getSubscribePath(String id) { + try { + return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("availableProtocols").getAsJsonObject() + .get(jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("protocol").getAsString()) + .getAsJsonObject().get("path").getAsString(); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return super.getSubscribePath(); + } + + public SubscriptionProtocol getSubscribeProtocol(String id) { + try { + if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("protocol").getAsString().equals("ws")) + return SubscriptionProtocol.WS; + + if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") + .getAsJsonObject().get("protocol").getAsString().equals("wss")) + return SubscriptionProtocol.WSS; + } catch (Exception e1) { + logger.debug(e1.getMessage()); + } + + return super.getSubscriptionProtocol(); + } + + public Set getUpdateIds() { + HashSet ret = new HashSet(); + + try { + for (Entry key : jsap.get("updates").getAsJsonObject().entrySet()) { + ret.add(key.getKey()); + } + } catch (Exception e) { + logger.warn(e.getMessage()); + } + + return ret; + } + + public Set getQueryIds() { + HashSet ret = new HashSet(); + + try { + for (Entry key : jsap.get("queries").getAsJsonObject().entrySet()) { + ret.add(key.getKey()); + } + } catch (Exception e) { + logger.warn(e.getMessage()); + } + + return ret; + } + + /** + *
+	 * "forcedBindings" : {
+					"variable_1" : {
+						"type" : "literal" ,
+						"value" : ""}
+					 ,
+					"variable_2" : {
+						"type" : "literal" ,
+						"value" : ""}
+					 ,
+					"variable_N" : {
+						"type" : "uri" ,
+						"value" : ""}
+				}
+	 * 
+ */ + public Bindings getUpdateBindings(String id) { + Bindings ret = new Bindings(); + + try { + for (Entry binding : jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject() + .get("forcedBindings").getAsJsonObject().entrySet()) { + RDFTerm bindingValue = null; + if (binding.getValue().getAsJsonObject().get("type").getAsString().equals("uri")) { + bindingValue = new RDFTermURI(binding.getValue().getAsJsonObject().get("value").getAsString()); + } else { + bindingValue = new RDFTermLiteral(binding.getValue().getAsJsonObject().get("value").getAsString()); + } + ret.addBinding(binding.getKey(), bindingValue); + } + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return ret; + } + + public Bindings getQueryBindings(String id) { + Bindings ret = new Bindings(); + + try { + for (Entry binding : jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject() + .get("forcedBindings").getAsJsonObject().entrySet()) { + RDFTerm bindingValue = null; + if (binding.getValue().getAsJsonObject().get("type").getAsString().equals("uri")) { + bindingValue = new RDFTermURI(binding.getValue().getAsJsonObject().get("value").getAsString()); + } else { + bindingValue = new RDFTermLiteral(binding.getValue().getAsJsonObject().get("value").getAsString()); + } + ret.addBinding(binding.getKey(), bindingValue); + } + } catch (Exception e) { + logger.debug(e.getMessage()); + } + + return ret; + } + + /** + *
+	 * "namespaces" : { 
+	 	"iot":"http://www.arces.unibo.it/iot#",
+	 	"rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},
+	 * 
+ */ + + public Set getPrefixes() { + HashSet ret = new HashSet(); + + try { + for (Entry key : jsap.get("namespaces").getAsJsonObject().entrySet()) + ret.add(key.getKey()); + } catch (Exception e) { + logger.debug(e.getMessage()); + } + return ret; + } + + public String getNamespaceURI(String prefix) { + try { + return jsap.get("namespaces").getAsJsonObject().get(prefix).getAsString(); + } catch (Exception e) { + logger.error(e.getMessage()); + return null; + } + } + + public String getFileName() { + return propertiesFile.getName(); + } + + public String printParameters() { + return jsap.toString(); + } + + public String getUpdateUrl(String id) { + String port = ""; + if (getUpdatePort(id) != -1) + port = ":" + getUpdatePort(id); + return getUpdateProtocol(id) + "://" + getUpdateHost(id) + port + getUpdatePath(id); + } + + public String getQueryUrl(String id) { + String port = ""; + if (getQueryPort(id) != -1) + port = ":" + getQueryPort(id); + return getQueryProtocol(id) + "://" + getQueryHost(id) + port + getQueryPath(id); + } + + public String getSubscribeUrl(String id) { + String scheme = ""; + String port = ""; + + SubscriptionProtocol prot = getSubscribeProtocol(id); + + if (prot.equals(SubscriptionProtocol.WS)) + scheme = "ws"; + else if (prot.equals(SubscriptionProtocol.WSS)) + scheme = "wss"; + + if (getSubscribePort(id) != -1) + port = ":" + getSubscribePort(id); + + return scheme + "://" + getSubscribeHost(id) + port + getSubscribePath(); + } + +} diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java similarity index 100% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java similarity index 97% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java index 119faeaf..a4e41a8f 100644 --- a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java @@ -48,13 +48,13 @@ public Consumer(ApplicationProfile appProfile, String subscribeID) throws SEPAPr throw new SEPAProtocolException(new IllegalArgumentException("Subscribe ID is null")); } - if (appProfile.subscribe(subscribeID) == null) { + if (appProfile.getSPARQLQuery(subscribeID) == null) { logger.fatal("SUBSCRIBE ID [" + subscribeID + "] not found in " + appProfile.getFileName()); throw new IllegalArgumentException( "SUBSCRIBE ID [" + subscribeID + "] not found in " + appProfile.getFileName()); } - sparqlSubscribe = appProfile.subscribe(subscribeID); + sparqlSubscribe = appProfile.getSPARQLQuery(subscribeID); protocolClient = new SPARQL11SEProtocol(appProfile,this); } diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java similarity index 74% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java index 037f073f..fe6e8fd1 100644 --- a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java @@ -33,12 +33,34 @@ public class GenericClient extends Client { public GenericClient(ApplicationProfile appProfile,ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException { super(appProfile); - protocolClient = new SPARQL11SEProtocol(appProfile, handler); } + public Response update(String ID,String SPARQL_UPDATE, Bindings forced,String usingGraphUri,String usingNamedGraphUri) throws SEPAProtocolException, SEPASecurityException { + protocolClient = new SPARQL11SEProtocol(appProfile,ID,true); + // TODO: move default timeout in jsap + UpdateRequest req = new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)); + // Graphs + req.setUsingGraphUri(usingGraphUri); + req.setNamedGraphUri(usingNamedGraphUri); + // Authentication + if (appProfile.isAuthenticationRequiredForUpdate(SPARQL_UPDATE)) { + + } + return protocolClient.update(req,5000); + } + + public Response update(String ID,String SPARQL_UPDATE, Bindings forced,int timeout) throws SEPAProtocolException, SEPASecurityException { + protocolClient = new SPARQL11SEProtocol(appProfile,ID,true); + return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)),timeout); + } + + public Response update(String SPARQL_UPDATE, Bindings forced,int timeout) { + return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)),timeout); + } public Response update(String SPARQL_UPDATE, Bindings forced) { - return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced))); + // TODO: move default timeout in jsap + return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)),5000); } public Response secureUpdate(String SPARQL_UPDATE, Bindings forced) { diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java similarity index 100% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java similarity index 100% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java similarity index 85% rename from client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java index a415bf29..1855134b 100644 --- a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java @@ -37,19 +37,24 @@ public class Producer extends Client implements IProducer { public Producer(ApplicationProfile appProfile,String updateID) throws SEPAProtocolException { super(appProfile); - if (appProfile.update(updateID) == null) { + if (appProfile.getSPARQLUpdate(updateID) == null) { logger.fatal("UPDATE ID [" +updateID+"] not found in "+appProfile.getFileName()); throw new IllegalArgumentException("UPDATE ID [" +updateID+"] not found in "+appProfile.getFileName()); } SPARQL_ID = updateID; - sparqlUpdate = appProfile.update(updateID); + sparqlUpdate = appProfile.getSPARQLUpdate(updateID); protocolClient = new SPARQL11SEProtocol(appProfile); } - public Response update(Bindings forcedBindings){ + public Response update(Bindings forcedBindings) { + //TODO : move default timeout in jsap + return update(forcedBindings,5000); + } + + public Response update(Bindings forcedBindings,int timeout){ if (sparqlUpdate == null || protocolClient == null) { logger.fatal("Producer not initialized"); return new ErrorResponse(-1,400,"Producer not initialized"); @@ -59,6 +64,6 @@ public Response update(Bindings forcedBindings){ logger.debug(" "+ SPARQL_ID+" ==> "+sparql); - return protocolClient.update(new UpdateRequest(sparql)); + return protocolClient.update(new UpdateRequest(sparql),timeout); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java new file mode 100644 index 00000000..b261482d --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java @@ -0,0 +1,48 @@ +package it.unibo.arces.wot.sepa.timing; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.commons.request.Request; +import it.unibo.arces.wot.sepa.commons.response.Response; + +public class Timings { + private static final Logger logger = LogManager.getLogger(); + + public static long getTime() { + return System.nanoTime(); + } + + public synchronized static void log(String tag,long start,long stop) { + String message = String.format("%d %s %d",start,tag,stop-start); + logger.log(Level.getLevel("timing"),message); + } + + public synchronized static void log(Request request) { + long start = getTime(); + + String tag; + if (request.isUpdateRequest()) tag = "REQUEST_UPDATE_"; + else if (request.isSubscribeRequest()) tag = "REQUEST_SUBSCRIBE_"; + else if(request.isQueryRequest()) tag = "REQUEST_QUERY_"; + else if(request.isUnsubscribeRequest()) tag = "REQUEST_UNSUBSCRIBE_"; + else tag = "REQUEST_UNKNOWN_"; + + log(tag+request.getToken(),start,start); + } + + public synchronized static void log(Response response) { + long start = getTime(); + + String tag; + if (response.isUpdateResponse()) tag = "RESPONSE_UPDATE_"; + else if (response.isSubscribeResponse()) tag = "RESPONSE_SUBSCRIBE_"; + else if(response.isQueryResponse()) tag = "RESPONSE_QUERY_"; + else if(response.isUnsubscribeResponse()) tag = "RESPONSE_UNSUBSCRIBE_"; + else if(response.isError()) tag = "RESPONSE_ERROR_"; + else tag = "RESPONSE_UNKNOWN_"; + + log(tag+response.getToken(),start,start); + } +} diff --git a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java b/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java deleted file mode 100644 index f518ae37..00000000 --- a/client-pac-pattern/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java +++ /dev/null @@ -1,321 +0,0 @@ -/* This class implements a JSON parser of an JSAP (JSON Application Profile) file - * - * Author: Luca Roffia (luca.roffia@unibo.it) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.pattern; - -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Set; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTerm; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; - -/** - * JSAP file example - * - *
- * {
- * "host" : "localhost" ,
- 	"sparql11protocol": {
- 		"host":"override default host", 	(optional)
-		"protocol": "http",
-		"port": 8000,					(optional)
-		"query": {
-			"path": "/query",
-			"method": "GET | POST | URL_ENCODED_POST",
-			"format": "JSON | XML | CSV"
-		},
-		"update": {
-			"path": "/update",
-			"method": "POST | URL_ENCODED_POST",
-			"format": "JSON | HTML"
-		}
-	},
-  	"sparql11seprotocol": {
-		"protocol": "ws",
-		"availableProtocols": {
-			"ws": {
-				"port": 9000,
-				"path": "/subscribe"
-			},
-			"wss": {
-				"port": 9443,
-				"path": "/subscribe"
-			}
-		},
-		"security": {
-			"register": "/oauth/register",
-			"tokenRequest": "/oauth/token",
-			"securePath": "/secure",
-			"client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs",
-			"client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg",
-			"jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT",
-			"expires": "04/5tRBT5n/VJ0XQASgs/w==",
-			"type": "XPrHEX2xHy+5IuXHPHigMw=="
-		},
-	"graphs": {
-		"default-graph-uri ": "http://default",
-		"named-graph-uri": "http://default",
-		"using-graph-uri": "http://default",
-		"using-named-graph-uri": "http://default"
-	},	
-	"extended" :{},
-	"namespaces" : {
-			"chat" : "http://wot.arces.unibo.it/chat#" ,
-			"rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#"},
-	"updates" : {
-			"UPDATE_1" : {
-				"sparql" : "..." ,
-				"forcedBindings" : {
-					"variable_1" : {
-						"type" : "literal" ,
-						"value" : ""}
-					 ,
-					"variable_2" : {
-						"type" : "literal" ,
-						"value" : ""}
-					 ,
-					"variable_N" : {
-						"type" : "uri" ,
-						"value" : ""}
-				}
-			}
-			 ,
-			"UPDATE_N" : {
-				"sparql" : "..."
-			}
-		}
-		 ,
-		"queries" : {
-			"QUERY_1" : {
-				"sparql" : "..." ,
-				"forcedBindings" : {
-					"variable_1" : {
-						"type" : "literal" ,
-						"value" : ""}
-					 ,
-					"variable_2" : {
-						"type" : "literal" ,
-						"value" : ""}
-					 ,
-					"variable_N" : {
-						"type" : "uri" ,
-						"value" : ""}
-				}
-			}
-			 ,
-			"QUERY_N" : {
-				"sparql" : "..."
-			}
-		}
-		}}
- * 
- */ -public class ApplicationProfile extends SPARQL11SEProperties { - public ApplicationProfile(String propertiesFile) throws SEPAPropertiesException { - super(propertiesFile); - } - - public ApplicationProfile(String propertiesFile, byte[] secret) throws SEPAPropertiesException { - super(propertiesFile, secret); - } - - protected Logger logger = LogManager.getLogger(); - - public JsonObject getExtendedData() { - try { - return jsap.get("extended").getAsJsonObject(); - } catch (Exception e) { - logger.error(e.getMessage()); - } - - return null; - } - - /** - *
-	 * "UPDATE_1" : {
-			"sparql" : "..." ,
-			"forcedBindings" : {
-				"variable_1" : {
-					"type" : "literal" ,
-					"value" : ""}
-				 ,
-				"variable_2" : {
-					"type" : "literal" ,
-					"value" : ""}
-				 ,
-				"variable_N" : {
-					"type" : "uri" ,
-					"value" : ""}
-			}
-		}
-	 * 
- */ - public String update(String updateID) { - try { - return jsap.get("updates").getAsJsonObject().get(updateID).getAsJsonObject().get("sparql").getAsString(); - } catch (Exception e) { - logger.error(e.getMessage()); - } - return null; - } - - public String subscribe(String subscribeID) { - try { - return jsap.get("queries").getAsJsonObject().get(subscribeID).getAsJsonObject().get("sparql").getAsString(); - } catch (Exception e) { - logger.error(e.getMessage()); - } - return null; - } - - public Set getUpdateIds() { - HashSet ret = new HashSet(); - - try { - for (Entry key : jsap.get("updates").getAsJsonObject().entrySet()) { - ret.add(key.getKey()); - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - - return ret; - } - - public Set getSubscribeIds() { - HashSet ret = new HashSet(); - - try { - for (Entry key : jsap.get("queries").getAsJsonObject().entrySet()) { - ret.add(key.getKey()); - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - - return ret; - } - - /** - *
-	 * "forcedBindings" : {
-					"variable_1" : {
-						"type" : "literal" ,
-						"value" : ""}
-					 ,
-					"variable_2" : {
-						"type" : "literal" ,
-						"value" : ""}
-					 ,
-					"variable_N" : {
-						"type" : "uri" ,
-						"value" : ""}
-				}
-	 * 
- */ - public Bindings updateBindings(String selectedValue) { - Bindings ret = new Bindings(); - - try { - for (Entry binding : jsap.get("updates").getAsJsonObject().get(selectedValue) - .getAsJsonObject().get("forcedBindings").getAsJsonObject().entrySet()) { - RDFTerm bindingValue = null; - if (binding.getValue().getAsJsonObject().get("type").getAsString().equals("uri")) { - bindingValue = new RDFTermURI(binding.getValue().getAsJsonObject().get("value").getAsString()); - } else { - bindingValue = new RDFTermLiteral(binding.getValue().getAsJsonObject().get("value").getAsString()); - } - ret.addBinding(binding.getKey(), bindingValue); - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - - return ret; - } - - public Bindings subscribeBindings(String selectedValue) { - Bindings ret = new Bindings(); - - try { - for (Entry binding : jsap.get("queries").getAsJsonObject().get(selectedValue) - .getAsJsonObject().get("forcedBindings").getAsJsonObject().entrySet()) { - RDFTerm bindingValue = null; - if (binding.getValue().getAsJsonObject().get("type").getAsString().equals("uri")) { - bindingValue = new RDFTermURI(binding.getValue().getAsJsonObject().get("value").getAsString()); - } else { - bindingValue = new RDFTermLiteral(binding.getValue().getAsJsonObject().get("value").getAsString()); - } - ret.addBinding(binding.getKey(), bindingValue); - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - - return ret; - } - - /** - *
-	 * "namespaces" : { 
-	 	"iot":"http://www.arces.unibo.it/iot#",
-	 	"rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},
-	 * 
- */ - - public Set getPrefixes() { - HashSet ret = new HashSet(); - - try { - for (Entry key : jsap.get("namespaces").getAsJsonObject().entrySet()) - ret.add(key.getKey()); - } catch (Exception e) { - logger.error(e.getMessage()); - } - return ret; - } - - public String getNamespaceURI(String prefix) { - try { - return jsap.get("namespaces").getAsJsonObject().get(prefix).getAsString(); - } catch (Exception e) { - logger.error(e.getMessage()); - return null; - } - } - - public String getFileName() { - return propertiesFile.getName(); - } - - public String printParameters() { - return jsap.toString(); - } -} diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java deleted file mode 100644 index cb5da782..00000000 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ /dev/null @@ -1,449 +0,0 @@ -/* This class implements the SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/) - * - * Author: Luca Roffia (luca.roffia@unibo.it) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.commons.protocol; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URLEncoder; -import java.nio.charset.Charset; - -import org.apache.http.Consts; -import org.apache.http.HttpEntity; -import org.apache.http.HttpStatus; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; - -import org.apache.logging.log4j.Logger; - -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; - -import org.apache.logging.log4j.LogManager; - -/** - * This class implements the SPARQL 1.1 Protocol - */ - -public class SPARQL11Protocol implements java.io.Closeable { - - /** The Constant logger. */ - private static final Logger logger = LogManager.getLogger("SPARQL11Protocol"); - - /** The m bean name. */ - protected static String mBeanName = "arces.unibo.SEPA.server:type=SPARQL11Protocol"; - - /** The properties. */ - protected SPARQL11Properties properties; - - // HTTP fields - final CloseableHttpClient httpClient = HttpClients.createDefault(); - final HttpPost updatePostRequest; - final HttpPost queryPostRequest; - HttpUriRequest queryRequest; - HttpUriRequest updateRequest; - - public SPARQL11Protocol(SPARQL11Properties properties) throws SEPAProtocolException { - if (properties == null) { - logger.fatal("Properties are null"); - throw new SEPAProtocolException(new IllegalArgumentException("Properties are null")); - } - this.properties = properties; - - // Create update POST request - try { - - updatePostRequest = new HttpPost(new URI("http", null, properties.getHost(), properties.getHttpPort(), - properties.getUpdatePath(), null, null)); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } - updatePostRequest.setHeader("Accept", properties.getUpdateAcceptHeader()); - updatePostRequest.setHeader("Content-Type", properties.getUpdateContentTypeHeader()); - - // Create query POST request - if (!properties.getQueryMethod().equals(HTTPMethod.GET)) { - try { - queryPostRequest = new HttpPost(new URI("http", null, properties.getHost(), properties.getHttpPort(), - properties.getQueryPath(), null, null)); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } - queryPostRequest.setHeader("Content-Type", properties.getQueryContentTypeHeader()); - queryPostRequest.setHeader("Accept", properties.getQueryAcceptHeader()); - } else - queryPostRequest = null; - } - - /** - * Implements a SPARQL 1.1 update operation - * (https://www.w3.org/TR/sparql11-protocol/) - * - *
-	 * update via URL-encoded POST 
-	 * - Method: POST
-	 * - Parameters: None
-	 * - Content Type: application/x-www-form-urlencoded
-	 * - Body: URL-encoded, ampersand-separated query parameters. 
-	 * 	update (exactly 1). 
-	 * 	using-graph-uri (0 or more). 
-	 * 	using-named-graph-uri (0 or more)
-	 * 
-	 * update via POST directly
-	 * - Method: POST
-	 * - Parameters: 
-	 * 	using-graph-uri (0 or more); 
-	 * 	using-named-graph-uri (0 or more)
-	 * - Content Type: application/sparql-update
-	 * - Body: Unencoded SPARQL update request string
-	 * 
- * - * 2.2.3 Specifying an RDF Dataset - *
-	 * SPARQL Update requests are executed against a Graph Store, a mutable
-	 * container of RDF graphs managed by a SPARQL service. The WHERE clause of a
-	 * SPARQL update DELETE/INSERT operation [UPDATE] matches against data in an RDF
-	 * Dataset, which is a subset of the Graph Store. The RDF Dataset for an update
-	 * operation may be specified either in the operation string itself using the
-	 * USING, USING NAMED, and/or WITH keywords, or it may be specified via the
-	 * using-graph-uri and using-named-graph-uri parameters.
-	 * 
-	 * It is an error to supply the using-graph-uri or using-named-graph-uri
-	 * parameters when using this protocol to convey a SPARQL 1.1 Update request
-	 * that contains an operation that uses the USING, USING NAMED, or WITH clause.
-	 * 
-	 * A SPARQL Update processor should treat each occurrence of the
-	 * using-graph-uri=g parameter in an update protocol operation as if a USING 
-	 * clause were included for every operation in the SPARQL 1.1 Update request.
-	 * Similarly, a SPARQL Update processor should treat each occurrence of the
-	 * using-named-graph-uri=g parameter in an update protocol operation as if a
-	 * USING NAMED  clause were included for every operation in the SPARQL 1.1
-	 * Update request.
-	 * 
-	 * UPDATE 2.2 update operation The response to an update request indicates
-	 * success or failure of the request via HTTP response status code.
-	 * 
- */ - public Response update(UpdateRequest req, int timeout) { - StringEntity requestEntity = null; - - CloseableHttpResponse httpResponse = null; - HttpEntity responseEntity = null; - int responseCode = 0; - String responseBody = null; - - try { - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) - .build(); - - // Set request entity - if (properties.getUpdateMethod().equals(HTTPMethod.GET)) { - // *********************** - // OpenLink VIRTUOSO PATCH - // *********************** - // SPARQL 1.1 Update are issued as GET requests using the "query" URL parameter - // The "default-graph-uri" parameter is REQUIRED - - String query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8") + "&format=" - + URLEncoder.encode(properties.getUpdateAcceptHeader(), "UTF-8"); - if (properties.getDefaultGraphURI() != null) { - query += "&default-graph-uri=" + URLEncoder.encode(properties.getDefaultGraphURI(), "UTF-8"); - } - - String url; - if (properties.getHttpPort() != -1) - url = "http://" + properties.getHost() + ":" + properties.getHttpPort() + properties.getUpdatePath() - + "?" + query; - else - url = "http://" + properties.getHost() + properties.getUpdatePath() + "?" + query; - - HttpGet queryGetRequest; - queryGetRequest = new HttpGet(url); - queryGetRequest.setConfig(requestConfig); - updateRequest = queryGetRequest; - - } else { - if (properties.getUpdateMethod().equals(HTTPMethod.URL_ENCODED_POST)) { - requestEntity = new StringEntity("update=" + URLEncoder.encode(req.getSPARQL(), "UTF-8")); - } else if (properties.getUpdateMethod().equals(HTTPMethod.POST)) { - requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); - } - updatePostRequest.setEntity(requestEntity); - - updatePostRequest.setConfig(requestConfig); - updateRequest = updatePostRequest; - } - - // Execute HTTP request - logger.debug("Execute SPARQL 1.1 UPDATE (timeout: " + timeout + " ms) " + updateRequest.toString(), - timeout); - long timing = System.nanoTime(); - httpResponse = httpClient.execute(updateRequest); - timing = System.nanoTime() - timing; - logger.debug("UPDATE_TIME (" + timing / 1000000 + " ms)"); - - // Status code - responseCode = httpResponse.getStatusLine().getStatusCode(); - - // Body - responseEntity = httpResponse.getEntity(); - responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); - EntityUtils.consume(responseEntity); - - } catch (IOException e) { - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } finally { - try { - if (httpResponse != null) - httpResponse.close(); - } catch (IOException e) { - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - - requestEntity = null; - responseEntity = null; - } - - if (responseCode >= 400) { - try { - return new ErrorResponse(req.getToken(), new JsonParser().parse(responseBody).getAsJsonObject()); - } catch (JsonParseException e) { - return new ErrorResponse(req.getToken(), responseCode, responseBody); - } - } - - return new UpdateResponse(req.getToken(), responseBody); - } - - /** - * Implements a SPARQL 1.1 query operation - * (https://www.w3.org/TR/sparql11-protocol/) - * - *
-	 * query via GET
-	 * - HTTP Method: GET
-	 * - Query String Parameters: query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
-	 * - Request Content Type: None
-	 * - Request Message Body: None
-	 * 
-	 * query via URL-encoded POST 
-	 * - HTTP Method: POST
-	 * - Query String Parameters: None
-	 * - Request Content Type: application/x-www-form-urlencoded
-	 * - Request Message Body: URL-encoded, ampersand-separated query parameters. query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
-	 * 
-	 * query via POST directly
-	 * - HTTP Method: POST
-	 * - Query String parameters: default-graph-uri (0 or more). named-graph-uri (0 or more)
-	 * - Request Content Type: application/sparql-query
-	 * - Request Message Body: Unencoded SPARQL update request string
-	 *
-	 * 2.1.4 Specifying an RDF Dataset
-	 * 
-	 * A SPARQL query is executed against an RDF Dataset. The RDF Dataset for a query may be specified either 
-	 * via the default-graph-uri and named-graph-uri parameters in the SPARQL Protocol or in the SPARQL query 
-	 * string using the FROM and FROM NAMED keywords. If different RDF Datasets are specified in both the protocol 
-	 * request and the SPARQL query string, then the SPARQL service must execute the query using the RDF Dataset 
-	 * given in the protocol request.
-	 * 
-	 * Note that a service may reject a query with HTTP response code 400 if the service does not allow protocol 
-	 * clients to specify the RDF Dataset. If an RDF Dataset is not specified in either the protocol request or 
-	 * the SPARQL query string, then implementations may execute the query against an implementation-defined default RDF dataset.
-	 * 
-	 * QUERY 2.1.5 Accepted Response Formats
-	 * 
-	 * Protocol clients should use HTTP content negotiation [RFC2616] to request
-	 * response formats that the client can consume. See below for more on
-	 * potential response formats.
-	 * 
-	 * 2.1.6 Success Responses
-	 * 
-	 * The SPARQL Protocol uses the response status codes defined in HTTP to
-	 * indicate the success or failure of an operation. Consult the HTTP
-	 * specification [RFC2616] for detailed definitions of each status code.
-	 * While a protocol service should use a 2XX HTTP response code for a
-	 * successful query, it may choose instead to use a 3XX response code as per
-	 * HTTP.
-	 * 
-	 * The response body of a successful query operation with a 2XX response is
-	 * either:
-	 * 
-	 * a SPARQL Results Document in XML, JSON, or CSV/TSV format (for SPARQL
-	 * Query forms SELECT and ASK); or, an RDF graph [RDF-CONCEPTS] serialized,
-	 * for example, in the RDF/XML syntax [RDF-XML], or an equivalent RDF graph
-	 * serialization, for SPARQL Query forms DESCRIBE and CONSTRUCT). The
-	 * content type of the response to a successful query operation must be the
-	 * media type defined for the format of the response body.
-	 * 
-	 * 2.1.7 Failure Responses
-	 * 
-	 * The HTTP response codes applicable to an unsuccessful query operation
-	 * include:
-	 * 
-	 * 400 if the SPARQL query supplied in the request is not a legal sequence
-	 * of characters in the language defined by the SPARQL grammar; or, 500 if
-	 * the service fails to execute the query. SPARQL Protocol services may also
-	 * return a 500 response code if they refuse to execute a query. This
-	 * response does not indicate whether the server may or may not process a
-	 * subsequent, identical request or requests. The response body of a failed
-	 * query request is implementation defined. Implementations may use HTTP
-	 * content negotiation to provide human-readable or machine-processable (or
-	 * both) information about the failed query request.
-	 * 
-	 * A protocol service may use other 4XX or 5XX HTTP response codes for other
-	 * failure conditions, as per HTTP.
-	 *
-	 * 
- */ - public Response query(QueryRequest req, int timeout) { - StringEntity requestEntity = null; - - CloseableHttpResponse httpResponse = null; - HttpEntity responseEntity = null; - int responseCode = 0; - String responseBody = null; - - long timing = 0; - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) - .build(); - - if (properties.getQueryMethod().equals(HTTPMethod.GET)) { - - String query = null; - try { - query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"); - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - if (properties.getDefaultGraphURI() != null) { - try { - query += "&default-graph-uri=" + URLEncoder.encode(properties.getDefaultGraphURI(), "UTF-8"); - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - } - - String url; - if (properties.getHttpPort() != -1) - url = "http://" + properties.getHost() + ":" + properties.getHttpPort() + properties.getQueryPath() - + "?" + query; - else - url = "http://" + properties.getHost() + properties.getQueryPath() + "?" + query; - - HttpGet queryGetRequest = null; - try{ - queryGetRequest = new HttpGet(url); - } - catch (IllegalArgumentException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - queryGetRequest.setConfig(requestConfig); - queryRequest = queryGetRequest; - - } else { - // Set request entity - if (properties.getQueryMethod().equals(HTTPMethod.URL_ENCODED_POST)) { - try { - requestEntity = new StringEntity("query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8")); - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - } else if (properties.getQueryMethod().equals(HTTPMethod.POST)) { - requestEntity = new StringEntity(req.getSPARQL(), "UTF-8"); - //requestEntity.setContentEncoding("UTF-8"); - } - queryPostRequest.setEntity(requestEntity); - queryPostRequest.setConfig(requestConfig); - queryRequest = queryPostRequest; - } - - queryRequest.addHeader("Accept", properties.getQueryAcceptHeader()); - // Execute HTTP request - logger.debug("Execute SPARQL 1.1 QUERY (timeout: " + timeout + " ms) " + queryRequest.toString(), timeout); - timing = System.nanoTime(); - try { - httpResponse = httpClient.execute(queryRequest); - - // Status code - responseCode = httpResponse.getStatusLine().getStatusCode(); - - // Body - responseEntity = httpResponse.getEntity(); - responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); - EntityUtils.consume(responseEntity); - - } catch (ClientProtocolException e1) { - logger.error(e1.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); - } catch (IOException e1) { - logger.error(e1.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); - } finally { - try { - if (httpResponse != null) - httpResponse.close(); - timing = System.nanoTime() - timing; - logger.debug("QUERY_TIME (" + timing / 1000000 + " ms)"); - } catch (IOException e) { - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - - requestEntity = null; - responseEntity = null; - } - - if (responseCode >= 400) { - try { - return new ErrorResponse(req.getToken(), new JsonParser().parse(responseBody).getAsJsonObject()); - } catch (JsonParseException | IllegalStateException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), responseCode, responseBody); - } - } - - return new QueryResponse(req.getToken(), new JsonParser().parse(responseBody).getAsJsonObject()); - } - - @Override - public void close() throws IOException { - httpClient.close(); - } -} diff --git a/engine/pom.xml b/engine/pom.xml index beaba1a8..d9ce3ebe 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -154,11 +154,6 @@ Java-WebSocket 1.3.8 - - it.unibo.arces.wot - commons - ${revision} - it.unibo.arces.wot client-api diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java index 75d84de1..412a36ac 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java @@ -74,6 +74,6 @@ public static String getUpTime() { public static void resetAll() { ProcessorBeans.reset(); SchedulerBeans.reset(); - SPUManagerBeans.reset(); + SubscribeProcessorBeans.reset(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java index 5d6d7e3c..1dd87b2d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java @@ -1,10 +1,11 @@ package it.unibo.arces.wot.sepa.engine.bean; -import java.time.Instant; import java.util.HashMap; import org.apache.http.nio.protocol.HttpAsyncExchange; +import it.unibo.arces.wot.sepa.timing.Timings; + public class HTTPHandlerBeans { private long requests = 0; @@ -14,13 +15,13 @@ public class HTTPHandlerBeans { private long validatingFailedRequests = 0; private long authorizingFailedRequests = 0; - private float requestHandlingTime = -1; + private long requestHandlingTime = -1; private float requestHandlingAverageTime = -1; - private float requestHandlingMinTime = -1; - private float requestHandlingMaxTime = -1; + private long requestHandlingMinTime = -1; + private long requestHandlingMaxTime = -1; private float handledRequests = 0; - private HashMap timings = new HashMap(); + private HashMap timings = new HashMap(); public void reset() { requests = 0; @@ -38,17 +39,19 @@ public void reset() { handledRequests = 0; } - public void newRequest(HttpAsyncExchange handler, Instant start) { + public long start(HttpAsyncExchange handler) { requests++; - timings.put(handler, start); + long start = Timings.getTime(); + timings.put(handler, start ); + return start; } - public float timings(HttpAsyncExchange handler) { + public synchronized long stop(HttpAsyncExchange handler) { handledRequests++; if (timings.get(handler) == null) return 0; - requestHandlingTime = Instant.now().toEpochMilli() - timings.get(handler).toEpochMilli(); + requestHandlingTime = Timings.getTime() - timings.get(handler); timings.remove(handler); if (requestHandlingMinTime == -1) @@ -71,19 +74,19 @@ else if (requestHandlingTime > requestHandlingMaxTime) } - public float getHandlingTime_ms() { + public long getHandlingTime() { return requestHandlingTime; } - public float getHandlingMinTime_ms() { + public long getHandlingMinTime() { return requestHandlingMinTime; } - public float getHandlingAvgTime_ms() { + public float getHandlingAvgTime() { return requestHandlingAverageTime; } - public float getHandlingMaxTime_ms() { + public long getHandlingMaxTime_ms() { return requestHandlingMaxTime; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SPUManagerBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java similarity index 98% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SPUManagerBeans.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java index abca5bfb..8923594c 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SPUManagerBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java @@ -2,7 +2,7 @@ import java.time.Instant; -public class SPUManagerBeans { +public class SubscribeProcessorBeans { private static long requests = 0; private static float minTime = -1; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index fa48fe00..54cb1963 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -238,7 +238,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException System.err.println(e1.getMessage()); System.exit(1); } - processor.setName("SEPA Processor"); + processor.setName("SEPA-Processor"); processor.start(); // SPARQL protocol service diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Timing.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Timing.java deleted file mode 100644 index 2d1f289d..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Timing.java +++ /dev/null @@ -1,37 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.dependability; - -import java.time.Instant; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.response.Response; - -public class Timing { - private static final Logger logger = LogManager.getLogger(""); - - public static void logTiming(Request request,String TAG,Instant instant) { - String type = " "; - - if(request.isQueryRequest()) type = "QUERY "; - else if (request.isUpdateRequest()) type = "UPDATE "; - else if (request.isSubscribeRequest()) type = "SUBSCRIBE "; - else type = "UNSUBSCRIBE "; - - logger.log(Level.getLevel("timing"), type + request.getToken()+ " "+TAG+" "+instant.toEpochMilli()); - } - - public static void logTiming(Response response,String TAG,Instant instant) { - String type = " "; - - if(response.isQueryResponse()) type = "QUERY "; - else if (response.isUpdateResponse()) type = "UPDATE "; - else if (response.isSubscribeResponse()) type = "SUBSCRIBE "; - else if (response.isUnsubscribeResponse()) type = "UNSUBSCRIBE "; - else type = "ERROR "; - - logger.log(Level.getLevel("timing"), type + response.getToken()+ " "+TAG+" "+instant.toEpochMilli()); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index 0f51f476..b57ce077 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -42,7 +42,7 @@ import org.apache.logging.log4j.LogManager; public class Processor extends Thread implements ProcessorMBean { - private final Logger logger = LogManager.getLogger("Processor"); + private final Logger logger = LogManager.getLogger(); // Processors private final UpdateProcessor updateProcessor; @@ -121,7 +121,7 @@ public void run() { queue.addResponse(ret); } }; - queryProcessing.setName("SEPA Query Processing Thread-" + request.getToken()); + queryProcessing.setName("SEPA-Query-Processing-Thread-" + request.getToken()); queryProcessing.start(); } else if (request.isSubscribeRequest()) { logger.info("Subscribe request #" + request.getToken()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index 23b05430..17cc5eb6 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -18,7 +18,6 @@ package it.unibo.arces.wot.sepa.engine.processing; -import java.time.Instant; import java.util.concurrent.Semaphore; import org.apache.logging.log4j.LogManager; @@ -30,22 +29,25 @@ import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; - import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; -import it.unibo.arces.wot.sepa.engine.dependability.Timing; +import it.unibo.arces.wot.sepa.timing.Timings; public class QueryProcessor { - private static final Logger logger = LogManager.getLogger("QueryProcessor"); + private static final Logger logger = LogManager.getLogger(); private SPARQL11Protocol endpoint; private Semaphore endpointSemaphore; + private SPARQL11Properties properties; public QueryProcessor(SPARQL11Properties properties,Semaphore endpointSemaphore) throws SEPAProtocolException { - endpoint = new SPARQL11Protocol(properties); + this.endpoint = new SPARQL11Protocol(properties); this.endpointSemaphore = endpointSemaphore; + this.properties = properties; } public synchronized Response process(QueryRequest req, int timeout) { + long start = Timings.getTime(); + if (endpointSemaphore != null) try { endpointSemaphore.acquire(); @@ -54,17 +56,13 @@ public synchronized Response process(QueryRequest req, int timeout) { } //QUERY the endpoint - long start = System.currentTimeMillis(); - Timing.logTiming(req, "ENDPOINT_REQUEST", Instant.now()); - Response ret = endpoint.query(req, timeout); - Timing.logTiming(req, "ENDPOINT_RESPONSE", Instant.now()); - long stop = System.currentTimeMillis(); + Response ret = endpoint.query(req, timeout,properties.getQueryMethod()); if (endpointSemaphore != null) endpointSemaphore.release(); + long stop = Timings.getTime(); logger.debug("Response: "+ret.toString()); - logger.debug("* QUERY PROCESSING ("+(stop-start)+" ms) *"); - + Timings.log("QUERY_PROCESSING_TIME", start, stop); ProcessorBeans.queryTimings(start, stop); return ret; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index bcae302f..9ae05c9b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -18,7 +18,6 @@ package it.unibo.arces.wot.sepa.engine.processing; -import java.time.Instant; import java.util.concurrent.Semaphore; import org.apache.logging.log4j.LogManager; @@ -31,21 +30,23 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; -import it.unibo.arces.wot.sepa.engine.dependability.Timing; +import it.unibo.arces.wot.sepa.timing.Timings; public class UpdateProcessor { - private static final Logger logger = LogManager.getLogger("UpdateProcessor"); + private static final Logger logger = LogManager.getLogger(); private SPARQL11Protocol endpoint; private Semaphore endpointSemaphore; + private SPARQL11Properties properties; public UpdateProcessor(SPARQL11Properties properties,Semaphore endpointSemaphore) throws SEPAProtocolException { endpoint = new SPARQL11Protocol(properties); this.endpointSemaphore = endpointSemaphore; + this.properties = properties; } public synchronized Response process(UpdateRequest req, int timeout) { - + long start = Timings.getTime(); if (endpointSemaphore != null) try { @@ -55,17 +56,13 @@ public synchronized Response process(UpdateRequest req, int timeout) { } // UPDATE the endpoint - long start = System.currentTimeMillis(); - Timing.logTiming(req, "ENDPOINT_REQUEST", Instant.now()); - Response ret = endpoint.update(req, timeout); - Timing.logTiming(req, "ENDPOINT_RESPONSE", Instant.now()); - long stop = System.currentTimeMillis(); + Response ret = endpoint.update(req, timeout,properties.getUpdateMethod()); if (endpointSemaphore != null) endpointSemaphore.release(); + long stop = Timings.getTime(); logger.debug("Response: "+ret.toString()); - logger.debug("* UPDATE PROCESSING ("+(stop-start)+" ms) *"); - + Timings.log("UPDATE_PROCESSING_TIME", start, stop); ProcessorBeans.updateTimings(start, stop); return ret; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index c21b1cc0..5a8a002f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -38,7 +38,7 @@ import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.bean.SPUManagerBeans; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; /** @@ -144,7 +144,7 @@ public void run() { logger.debug("* PROCESSING *"); //Asynchronous processing and waiting for result - notify = processInternal(updateResponse,SPUManagerBeans.getSPUProcessingTimeout()); + notify = processInternal(updateResponse,SubscribeProcessorBeans.getSPUProcessingTimeout()); // Notify event handler if (handler != null) { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java index cdf7743a..b863bc40 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java @@ -8,7 +8,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.engine.bean.SPUManagerBeans; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; public class SPUSync { private final Logger logger = LogManager.getLogger("SPUSync"); @@ -29,7 +29,7 @@ public void waitEndOfProcessing() { while (!processingSpus.isEmpty()) { logger.info(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); try { - processingSpus.wait(SPUManagerBeans.getSPUProcessingTimeout()); + processingSpus.wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); } catch (InterruptedException e) { return; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java index 00a0d5df..54b2e479 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java @@ -36,19 +36,18 @@ import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -import it.unibo.arces.wot.sepa.engine.bean.SPUManagerBeans; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.EventHandler; -public class SubscribeProcessor implements SPUManagerMBean { +public class SubscribeProcessor implements SubscribeProcessorMBean { private final Logger logger = LogManager.getLogger("SubscribeProcessor"); private final Subscriber subscriber; private final Unsubcriber unsubscriber; private SPARQL11Properties endpointProperties; - private SPUManager spuManager = new SPUManager(); // Request queue @@ -66,7 +65,7 @@ public SubscribeProcessor(SPARQL11Properties endpointProperties, EnginePropertie this.endpointSemaphore = endpointSemaphore; SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); - SPUManagerBeans.setSPUProcessingTimeout(engineProperties.getSPUProcessingTimeout()); + SubscribeProcessorBeans.setSPUProcessingTimeout(engineProperties.getSPUProcessingTimeout()); this.subscriber = new Subscriber(subscribeQueue,spuManager); this.unsubscriber = new Unsubcriber(unsubscribeQueue,spuManager); @@ -88,7 +87,7 @@ public void stop(){ public Response subscribe(SubscribeRequest req, EventHandler handler) { logger.debug(req.toString()); - SPUManagerBeans.subscribeRequest(); + SubscribeProcessorBeans.subscribeRequest(); // TODO: choose different kinds of SPU based on subscribe request SPU spu = null; @@ -117,7 +116,7 @@ public Response subscribe(SubscribeRequest req, EventHandler handler) { public Response unsubscribe(UnsubscribeRequest req) { logger.debug(req); - SPUManagerBeans.unsubscribeRequest(); + SubscribeProcessorBeans.unsubscribeRequest(); String spuid = req.getSubscribeUUID(); @@ -148,78 +147,78 @@ public void process(UpdateResponse update) { spuSync.waitEndOfProcessing(); Instant stop = Instant.now(); - SPUManagerBeans.timings(start, stop); + SubscribeProcessorBeans.timings(start, stop); logger.info("*** PROCESSING SUBSCRIPTIONS END *** "); } @Override public long getRequests() { - return SPUManagerBeans.getRequests(); + return SubscribeProcessorBeans.getRequests(); } @Override public long getSPUs_current() { - return SPUManagerBeans.getSPUs_current(); + return SubscribeProcessorBeans.getSPUs_current(); } @Override public long getSPUs_max() { - return SPUManagerBeans.getSPUs_max(); + return SubscribeProcessorBeans.getSPUs_max(); } @Override public float getSPUs_time() { - return SPUManagerBeans.getSPUs_time(); + return SubscribeProcessorBeans.getSPUs_time(); } @Override public void reset() { - SPUManagerBeans.reset(); + SubscribeProcessorBeans.reset(); } @Override public void setKeepalive(int t) { - SPUManagerBeans.setKeepalive(t); + SubscribeProcessorBeans.setKeepalive(t); } @Override public int getKeepalive() { - return SPUManagerBeans.getKeepalive(); + return SubscribeProcessorBeans.getKeepalive(); } @Override public float getSPUs_time_min() { - return SPUManagerBeans.getSPUs_time_min(); + return SubscribeProcessorBeans.getSPUs_time_min(); } @Override public float getSPUs_time_max() { - return SPUManagerBeans.getSPUs_time_max(); + return SubscribeProcessorBeans.getSPUs_time_max(); } @Override public float getSPUs_time_average() { - return SPUManagerBeans.getSPUs_time_averaae(); + return SubscribeProcessorBeans.getSPUs_time_averaae(); } @Override public long getSubscribeRequests() { - return SPUManagerBeans.getSubscribeRequests(); + return SubscribeProcessorBeans.getSubscribeRequests(); } @Override public long getUnsubscribeRequests() { - return SPUManagerBeans.getUnsubscribeRequests(); + return SubscribeProcessorBeans.getUnsubscribeRequests(); } @Override public long getSPUProcessingTimeout() { - return SPUManagerBeans.getSPUProcessingTimeout(); + return SubscribeProcessorBeans.getSPUProcessingTimeout(); } @Override public void setSPUProcessingTimeout(long t) { - SPUManagerBeans.setActiveSPUs(t); + SubscribeProcessorBeans.setActiveSPUs(t); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManagerMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessorMBean.java similarity index 92% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManagerMBean.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessorMBean.java index 62d7c994..91a5c853 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManagerMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessorMBean.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -public interface SPUManagerMBean { +public interface SubscribeProcessorMBean { public long getRequests(); public long getSubscribeRequests(); public long getUnsubscribeRequests(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java index 7839bce0..d67406dc 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -import it.unibo.arces.wot.sepa.engine.bean.SPUManagerBeans; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,7 +14,7 @@ public class Subscriber extends Thread { private final SPUManager spuManager; public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ - super("SEPA SPU Subscriber"); + super("SEPA-SPU-Subscriber"); this.subscriptionQueue = subscriptionQueue; spuManager = manager; } @@ -32,7 +32,7 @@ public void run() { spuManager.register(spu); - SPUManagerBeans.setActiveSPUs(spuManager.size()); + SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); logger.debug(spu.getUUID() + " ACTIVATED (total: " + spuManager.size() + ")"); } catch (InterruptedException e) { logger.info(e); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java index 162869da..aa3ca88b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -import it.unibo.arces.wot.sepa.engine.bean.SPUManagerBeans; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,7 +20,7 @@ public class Unsubcriber extends Thread { private final AtomicBoolean end = new AtomicBoolean(false); public Unsubcriber(BlockingQueue unsubscribeQueue, SPUManager manager){ - super("SEPA SPU Unsubscriber"); + super("SEPA-SPU-Unsubscriber"); this.unsubscribeQueue = unsubscribeQueue; spuManager = manager; } @@ -35,7 +35,7 @@ public void run() { spuManager.unRegister(spuUID); - SPUManagerBeans.setActiveSPUs(spuManager.size()); + SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); logger.debug("Active SPUs: " + spuManager.size()); } catch (InterruptedException e) { logger.info(e); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java index e8be46ed..c81142c4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java @@ -1,7 +1,6 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; import java.io.IOException; -import java.time.Instant; import org.apache.http.HttpException; import org.apache.http.HttpRequest; @@ -19,12 +18,12 @@ import it.unibo.arces.wot.sepa.engine.bean.HTTPHandlerBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.dependability.CORSManager; -import it.unibo.arces.wot.sepa.engine.dependability.Timing; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; +import it.unibo.arces.wot.sepa.timing.Timings; public abstract class SPARQL11Handler implements HttpAsyncRequestHandler, SPARQL11HandlerMBean { - private static final Logger logger = LogManager.getLogger("SPARQL11Handler"); + private static final Logger logger = LogManager.getLogger(); private Scheduler scheduler; @@ -77,9 +76,6 @@ public HttpAsyncRequestConsumer processRequest(HttpRequest request, @Override public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpContext context) throws HttpException, IOException { - - Instant start = Instant.now(); - // CORS if (!corsHandling(httpExchange)) { jmx.corsFailed(); @@ -115,12 +111,10 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont jmx.authorizingFailed(); return; } - - Timing.logTiming(sepaRequest, "REQUEST", start); - Timing.logTiming(sepaRequest, "SCHEDULING", Instant.now()); // Schedule request - scheduler.schedule(sepaRequest, new SPARQL11ResponseHandler(httpExchange, jmx, start)); + Timings.log(sepaRequest); + scheduler.schedule(sepaRequest, new SPARQL11ResponseHandler(httpExchange, jmx)); } @Override @@ -136,17 +130,17 @@ public void reset() { @Override public float getHandlingTime_ms() { - return jmx.getHandlingTime_ms(); + return jmx.getHandlingTime(); } @Override public float getHandlingMinTime_ms() { - return jmx.getHandlingMinTime_ms(); + return jmx.getHandlingMinTime(); } @Override public float getHandlingAvgTime_ms() { - return jmx.getHandlingAvgTime_ms(); + return jmx.getHandlingAvgTime(); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java index d65e8b28..1ac680b9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java @@ -1,39 +1,32 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; -import java.time.Instant; - import org.apache.http.HttpStatus; import org.apache.http.nio.protocol.HttpAsyncExchange; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -//import com.google.gson.JsonObject; -//import com.google.gson.JsonParser; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -//import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.bean.HTTPHandlerBeans; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.engine.dependability.Timing; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; +import it.unibo.arces.wot.sepa.timing.Timings; public class SPARQL11ResponseHandler implements ResponseHandler { - protected final Logger logger = LogManager.getLogger("SPARQL11ResponseHandler"); + protected final Logger logger = LogManager.getLogger(); private HttpAsyncExchange handler; private HTTPHandlerBeans jmx; - public SPARQL11ResponseHandler(HttpAsyncExchange httpExchange, HTTPHandlerBeans jmx, Instant start) { + public SPARQL11ResponseHandler(HttpAsyncExchange httpExchange, HTTPHandlerBeans jmx) { this.handler = httpExchange; this.jmx = jmx; - jmx.newRequest(handler,start); + jmx.start(handler); } @Override public void sendResponse(Response response) { - Timing.logTiming(response, "RESPONDING", Instant.now()); - if (response.isError()) { ErrorResponse err = (ErrorResponse) response; HttpUtilities.sendResponse(handler,err.getErrorCode(),response.toString()); @@ -41,8 +34,9 @@ public void sendResponse(Response response) { else HttpUtilities.sendResponse(handler, HttpStatus.SC_OK, response.toString()); - Timing.logTiming(response, "RESPONSE_SENT", Instant.now()); - logger.info("Response #"+response.getToken()+" ("+jmx.timings(handler)+" ms)"); + Timings.log(response); + jmx.stop(handler); + logger.debug("Response #"+response.getToken()+" sent"); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java index dfc77d0b..28697efe 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java @@ -77,11 +77,26 @@ protected Request parse(HttpAsyncExchange exchange) { if (contentType.equals("application/sparql-update")) { logger.debug("update via POST directly"); - return new UpdateRequest(body); + UpdateRequest ret = new UpdateRequest(body); + try { + String requestUri = exchange.getRequest().getRequestLine().getUri(); + if (requestUri.indexOf('?') != -1) { + String[] split = requestUri.split("\\?"); + if (split.length == 2) { + Map params = HttpUtilities.splitQuery(split[1]); + if (params.get("using-graph-uri") != null) ret.setUsingGraphUri(params.get("using-graph-uri")); + if (params.get("named-graph-uri") != null) ret.setNamedGraphUri(params.get("named-graph-uri")); + } + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, e.getMessage()); + } + + return ret; } else if (contentType.equals("application/x-www-form-urlencoded")) { try { - Map params = HttpUtilities.splitQuery(body); - + Map params = HttpUtilities.splitQuery(body); logger.debug("update via URL ENCODED POST directly: "+params.get("update")); if (params.get("update") != null) return new UpdateRequest(params.get("update")); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 419d57b6..19640d35 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -19,7 +19,6 @@ package it.unibo.arces.wot.sepa.engine.scheduling; import java.io.IOException; -import java.time.Instant; import java.util.HashMap; import java.util.Vector; @@ -35,14 +34,14 @@ import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.engine.dependability.Timing; +import it.unibo.arces.wot.sepa.timing.Timings; /** * This class represents the scheduler of the SPARQL Event Processing Engine */ public class Scheduler extends Thread implements SchedulerMBean { - private static final Logger logger = LogManager.getLogger("Scheduler"); + private static final Logger logger = LogManager.getLogger(); // Request tokens private Vector tokens = new Vector(); @@ -72,7 +71,7 @@ public Scheduler(EngineProperties properties,SchedulerRequestResponseQueue queue SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SchedulerBeans.setQueueSize(properties.getSchedulingQueueSize()); - this.setName("SEPA Response Scheduler"); + this.setName("SEPA-Scheduler"); } public synchronized void schedule(Request request, ResponseHandler handler) { @@ -91,7 +90,7 @@ public synchronized void schedule(Request request, ResponseHandler handler) { queue.addRequest(new ScheduledRequest(token, request, handler)); - Timing.logTiming(request, "SCHEDULED", Instant.now()); + Timings.log(request); } /** diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java index c03b6a11..a8cab2db 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java @@ -9,11 +9,7 @@ public class SchedulerRequestResponseQueue { private LinkedBlockingQueue responses = new LinkedBlockingQueue(); public void addRequest(ScheduledRequest req) { - try { - requests.put(req); - } catch (InterruptedException e) { - return; - } + requests.offer(req); } public ScheduledRequest waitRequest() throws InterruptedException { @@ -21,11 +17,7 @@ public ScheduledRequest waitRequest() throws InterruptedException { } public void addResponse(Response res) { - try { - responses.put(res); - } catch (InterruptedException e) { - return; - } + responses.offer(res); } public Response waitResponse() throws InterruptedException { diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index a7b6709c..3be39197 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -1,21 +1,31 @@ - + + - + - + diff --git a/tools/pom.xml b/tools/pom.xml index 4c7ce67b..b90a8d86 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -43,21 +43,11 @@ org.eclipse.paho.client.mqttv3 1.1.1 - - it.unibo.arces.wot - commons - ${revision} - it.unibo.arces.wot client-api ${revision} - - it.unibo.arces.wot - client-pac-pattern - ${revision} - true diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java index b558920b..198a968b 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java @@ -16,9 +16,9 @@ public class BasicClient extends ChatClient { private int notifications = 0; private int expectedNotifications = 0; - public BasicClient(String userURI, Users users, int messages,Timings timings) + public BasicClient(String userURI, Users users, int messages) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(userURI,timings); + super(userURI); this.user = userURI; this.users = users; this.messages = messages; diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java index 12847931..81bbcfc1 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java @@ -11,11 +11,11 @@ public abstract class ChatClient implements Runnable { private Remover remover; protected String userURI; - public ChatClient(String userURI,Timings timings) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + public ChatClient(String userURI) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { this.userURI = userURI; - sender = new Sender(userURI,timings); - receiver = new Receiver(userURI,timings,this); - remover = new Remover(userURI,timings,this); + sender = new Sender(userURI); + receiver = new Receiver(userURI,this); + remover = new Remover(userURI,this); } public boolean joinChat() { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java index aed660a5..9370e3b8 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java @@ -7,9 +7,9 @@ public class PingPongClient extends BasicClient { private int index = 0; - public PingPongClient(String userURI, Users users,Timings timings) + public PingPongClient(String userURI, Users users) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(userURI, users,1,timings); + super(userURI, users,1); } @Override diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java index c730a341..7fe6e797 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java @@ -21,17 +21,15 @@ public class Receiver extends Aggregator { private Bindings message = new Bindings(); private boolean joined = false; - private Timings timings; private ChatClient client; - public Receiver(String receiverURI,Timings timings,ChatClient client) + public Receiver(String receiverURI,ChatClient client) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(new ApplicationProfile("chat.jsap"), "SENT", "SET_RECEIVED"); message.addBinding("receiver", new RDFTermURI(receiverURI)); - this.timings = timings; this.client = client; } @@ -63,18 +61,15 @@ public void onAddedResults(BindingsResults results) { // Variables: ?message ?sender ?name ?text ?time for (Bindings bindings : results.getBindings()) { logger.info("SENT "+bindings.getBindingValue("message")); - - timings.sent(bindings.getBindingValue("message"),bindings.getBindingValue("sender"),message.getBindingValue("receiver"),bindings.getBindingValue("text")); - client.onMessage(bindings.getBindingValue("sender"), bindings.getBindingValue("text")); // Set received Bindings setReceived = new Bindings(); setReceived.addBinding("message", new RDFTermURI(bindings.getBindingValue("message"))); - timings.setReceivedStart(bindings.getBindingValue("message")); + update(setReceived); - timings.setReceivedStop(bindings.getBindingValue("message")); + } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java index 0d4a1157..d97edfc7 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java @@ -21,14 +21,11 @@ public class Remover extends Aggregator { private Bindings sender = new Bindings(); private boolean joined = false; - private Timings timings; - public Remover(String senderURI,Timings timings,ChatClient client) throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { + public Remover(String senderURI,ChatClient client) throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { super(new ApplicationProfile("chat.jsap"), "RECEIVED", "REMOVE"); sender.addBinding("sender", new RDFTermURI(senderURI)); - - this.timings = timings; } public boolean joinChat() { @@ -64,11 +61,7 @@ public void onAddedResults(BindingsResults results) { logger.info("RECEIVED From: "+bindings.getBindingValue("message")); // Variables: ?message ?time - timings.received(bindings.getBindingValue("message")); - - timings.removeStart(bindings.getBindingValue("message")); update(bindings); - timings.removeStop(bindings.getBindingValue("message")); } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java index 476e8ade..42d291c6 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java @@ -20,8 +20,6 @@ public class SEPAChatTest { private int MESSAGES = 10; private Users users; private static List clients = new ArrayList(); - - private static Timings timings = new Timings(); private enum CLIENT_TYPE { PING_PONG, BASIC @@ -67,13 +65,13 @@ public boolean start() throws SEPAProtocolException, SEPASecurityException, SEPA ChatClient client = null; switch (type) { case BASIC: - client = new BasicClient(user, users,MESSAGES,timings); + client = new BasicClient(user, users,MESSAGES); break; case PING_PONG: - client = new PingPongClient(user, users,timings); + client = new PingPongClient(user, users); break; default: - client = new BasicClient(user, users,MESSAGES,timings); + client = new BasicClient(user, users,MESSAGES); } Thread th = new Thread(client); @@ -99,8 +97,6 @@ public static void main(String[] args) for (Thread th:clients) th.join(60000); - timings.logToFile(); - System.exit(0); } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java index b5047988..5cf4948f 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java @@ -12,20 +12,18 @@ import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; import it.unibo.arces.wot.sepa.pattern.Producer; +import it.unibo.arces.wot.sepa.timing.Timings; public class Sender extends Producer { private static final Logger logger = LogManager.getLogger(); private Bindings message = new Bindings(); - private Timings timings; - public Sender(String senderURI,Timings timings) + public Sender(String senderURI) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(new ApplicationProfile("chat.jsap"), "SEND"); message.addBinding("sender", new RDFTermURI(senderURI)); - - this.timings = timings; } public boolean sendMessage(String receiverURI,String text) { @@ -34,9 +32,11 @@ public boolean sendMessage(String receiverURI,String text) { message.addBinding("receiver", new RDFTermURI(receiverURI)); message.addBinding("text", new RDFTermLiteral(text)); - timings.startSend(message.getBindingValue("sender"),message.getBindingValue("receiver"),message.getBindingValue("text")); + long start = Timings.getTime(); boolean ret = update(message).isUpdateResponse(); - timings.stopSend(message.getBindingValue("sender"),message.getBindingValue("receiver"),message.getBindingValue("text")); + long stop = Timings.getTime(); + String msg = message.getBindingValue("sender")+message.getBindingValue("receiver")+message.getBindingValue("text"); + Timings.log(msg.replace(" ", "_"), start, stop); return ret; } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Timings.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Timings.java deleted file mode 100644 index 58e8a560..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Timings.java +++ /dev/null @@ -1,106 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import java.util.Date; -import java.util.HashMap; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class Timings { - private static final Logger logger = LogManager.getLogger(); - - class Timing { - public long startSend; - public long stopSend; - - public long setReceivedStart; - public long setReceivedStop; - - public long removeStart; - public long removeStop; - - public long sent; - public long received; - } - - HashMap updates = new HashMap(); - HashMap messages = new HashMap(); - - public synchronized void startSend(String from, String to, String message) { - Timing t = new Timing(); - t.startSend = new Date().getTime(); - synchronized (updates) { - updates.put(from + to + message, t); - } - } - - public synchronized void stopSend(String from, String to, String message) { - if (updates.get(from + to + message) == null) - return; - synchronized (updates) { - updates.get(from + to + message).stopSend = new Date().getTime(); - } - } - - public synchronized void sent(String messageURI, String from, String to, String message) { - if (updates.get(from + to + message) == null) - return; - - synchronized (updates) { - updates.get(from + to + message).sent = new Date().getTime(); - } - synchronized (messages) { - messages.put(messageURI, updates.get(from + to + message)); - } - } - - public synchronized void setReceivedStart(String messageURI) { - if (messages.get(messageURI) == null) - return; - synchronized (messages) { - messages.get(messageURI).setReceivedStart = new Date().getTime(); - } - } - - public synchronized void setReceivedStop(String messageURI) { - if (messages.get(messageURI) == null) - return; - synchronized (messages) { - messages.get(messageURI).setReceivedStop = new Date().getTime(); - } - } - - public synchronized void received(String messageURI) { - if (messages.get(messageURI) == null) - return; - synchronized (messages) { - messages.get(messageURI).received = new Date().getTime(); - } - } - - public synchronized void removeStart(String messageURI) { - if (messages.get(messageURI) == null) - return; - synchronized (messages) { - messages.get(messageURI).removeStart = new Date().getTime(); - } - } - - public synchronized void removeStop(String messageURI) { - if (messages.get(messageURI) == null) - return; - synchronized (messages) { - messages.get(messageURI).removeStop = new Date().getTime(); - } - } - - public synchronized void logToFile() { - synchronized (messages) { - logger.log(Level.getLevel("timing"),"Send-start Send-stop Sent Set-received-start Set-received-stop Received Remove-start Remove-stop");} - for (Timing t : messages.values()) { - String message = String.format("%d %d %d %d %d %d %d %d %d",t.startSend, t.stopSend,t.sent,t.setReceivedStart,t.setReceivedStop,t.received,t.removeStart,t.removeStop); - logger.log(Level.getLevel("timing"),message); - } - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java index d300bc36..e57c257e 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java @@ -40,15 +40,15 @@ public class UpdateQueryTest extends GenericClient { public UpdateQueryTest(ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(new ApplicationProfile("chat.jsap"), handler); - SEND = appProfile.update("SEND"); - SET_RECEIVED = appProfile.update("SET_RECEIVED"); - REMOVE = appProfile.update("REMOVE"); - DELETE_ALL = appProfile.update("DELETE_ALL"); - REGISTER_USER = appProfile.update("REGISTER_USER"); - - SENT = appProfile.subscribe("SENT"); - RECEIVED = appProfile.subscribe("RECEIVED"); - USERS = appProfile.subscribe("USERS"); + SEND = appProfile.getSPARQLUpdate("SEND"); + SET_RECEIVED = appProfile.getSPARQLUpdate("SET_RECEIVED"); + REMOVE = appProfile.getSPARQLUpdate("REMOVE"); + DELETE_ALL = appProfile.getSPARQLUpdate("DELETE_ALL"); + REGISTER_USER = appProfile.getSPARQLUpdate("REGISTER_USER"); + + SENT = appProfile.getSPARQLQuery("SENT"); + RECEIVED = appProfile.getSPARQLQuery("RECEIVED"); + USERS = appProfile.getSPARQLQuery("USERS"); clients = appProfile.getExtendedData().get("clients").getAsInt(); diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index afe758f6..c9007733 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -86,6 +86,7 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; @@ -108,6 +109,8 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.border.EtchedBorder; +import javax.swing.border.LineBorder; +import java.awt.SystemColor; public class Dashboard { private static final Logger logger = LogManager.getLogger("Dashboard"); @@ -237,24 +240,6 @@ public void clear() { ApplicationProfile appProfile; - private JLabel labelHttpPort; - - private JLabel labelHttpsPort; - - private JLabel labelWsPort; - - private JLabel labelWssPort; - - private JLabel labelUpdatePath; - - private JLabel labelQueryPath; - - private JLabel labelSubscribePath; - - private JLabel labelUrl; - - private JLabel labelSecurePath; - private class CopyAction extends AbstractAction { /** @@ -417,13 +402,14 @@ public void setResults(ARBindingsResults res, String spuid) { if (res == null) return; -// Date date = new Date(); -// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); -// String timestamp = sdf.format(date); + // Date date = new Date(); + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + // String timestamp = sdf.format(date); ArrayList vars = res.getAddedBindings().getVariables(); for (String var : res.getRemovedBindings().getVariables()) { - if (!vars.contains(var)) vars.add(var); + if (!vars.contains(var)) + vars.add(var); } if (!columns.containsAll(vars) || columns.size() != vars.size()) { @@ -438,18 +424,18 @@ public void setResults(ARBindingsResults res, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getBindingValue(var), sol.isLiteral(var), true)); } - //row.put("", new BindingValue(timestamp, false, true)); + // row.put("", new BindingValue(timestamp, false, true)); rows.add(row); } } - + if (res.getRemovedBindings() != null) { for (Bindings sol : res.getRemovedBindings().getBindings()) { HashMap row = new HashMap(); for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getBindingValue(var), sol.isLiteral(var), false)); } - //row.put("", new BindingValue(timestamp, false, false)); + // row.put("", new BindingValue(timestamp, false, false)); rows.add(row); } } @@ -518,15 +504,15 @@ public void setAddedResults(BindingsResults bindingsResults, String spuid) { if (bindingsResults == null) return; - //Date date = new Date(); - //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - //String timestamp = sdf.format(date); + // Date date = new Date(); + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + // String timestamp = sdf.format(date); ArrayList vars = bindingsResults.getVariables(); if (!columns.containsAll(vars) || columns.size() != vars.size()) { columns.clear(); - //vars.add(""); + // vars.add(""); columns.addAll(vars); super.fireTableStructureChanged(); } @@ -536,7 +522,7 @@ public void setAddedResults(BindingsResults bindingsResults, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getBindingValue(var), sol.isLiteral(var), true)); } - //row.put("", new BindingValue(timestamp, false, true)); + // row.put("", new BindingValue(timestamp, false, true)); rows.add(row); } @@ -701,18 +687,6 @@ private boolean loadSAP(String file) { file = path; } - labelUrl.setText("---"); - - labelHttpPort.setText("---"); - labelHttpsPort.setText("---"); - labelWsPort.setText("---"); - labelWssPort.setText("---"); - - labelUpdatePath.setText("---"); - labelQueryPath.setText("---"); - labelSubscribePath.setText("---"); - labelSecurePath.setText("---"); - SPARQLSubscribe.setText(""); SPARQLUpdate.setText(""); namespacesDM.getDataVector().clear(); @@ -751,23 +725,11 @@ private boolean loadSAP(String file) { } // Loading subscribes - for (String subscribe : appProfile.getSubscribeIds()) { + for (String subscribe : appProfile.getQueryIds()) { // subscribeListDM.addElement(subscribe); subscribeListDM.add(subscribe); } - labelUrl.setText(appProfile.getHost()); - - labelHttpPort.setText(String.format("%d", appProfile.getHttpPort())); - labelHttpsPort.setText(String.format("%d", appProfile.getHttpsPort())); - labelWsPort.setText(String.format("%d", appProfile.getWsPort())); - labelWssPort.setText(String.format("%d", appProfile.getWssPort())); - - labelUpdatePath.setText(appProfile.getUpdatePath()); - labelQueryPath.setText(appProfile.getQueryPath()); - labelSubscribePath.setText(appProfile.getSubscribePath()); - labelSecurePath.setText(appProfile.getSecurePath()); - lblInfo.setText("JSAP loaded"); lblInfo.setToolTipText("JSAP loaded"); @@ -838,251 +800,175 @@ public boolean isCellEditable(int row, int column) { propertiesDM.setColumnIdentifiers(propertiesHeader); frmSepaDashboard = new JFrame(); - frmSepaDashboard.setTitle(versionLabel); + frmSepaDashboard.setTitle("SEPA Dashboard Ver 0.9.1"); frmSepaDashboard.setBounds(100, 100, 925, 768); frmSepaDashboard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[] { 925, 0 }; - gridBagLayout.rowHeights = new int[] { 78, 651, 39, 0 }; + gridBagLayout.rowHeights = new int[] { 651, 39, 0 }; gridBagLayout.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gridBagLayout.rowWeights = new double[] { 0.0, 1.0, 0.0, Double.MIN_VALUE }; + gridBagLayout.rowWeights = new double[] { 1.0, 0.0, Double.MIN_VALUE }; frmSepaDashboard.getContentPane().setLayout(gridBagLayout); - JPanel configuration = new JPanel(); - GridBagConstraints gbc_configuration = new GridBagConstraints(); - gbc_configuration.insets = new Insets(0, 0, 5, 0); - gbc_configuration.fill = GridBagConstraints.BOTH; - gbc_configuration.gridx = 0; - gbc_configuration.gridy = 0; - frmSepaDashboard.getContentPane().add(configuration, gbc_configuration); - configuration - .setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "SPARQL 1.1 SE Protocol configuration", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); - GridBagLayout gbl_configuration = new GridBagLayout(); - gbl_configuration.columnWidths = new int[] { 46, 45, 31, 20, 0, 24, 0, 0, 0, 0, 0, 0, 37, 0 }; - gbl_configuration.rowHeights = new int[] { 9, -25, 0 }; - gbl_configuration.columnWeights = new double[] { 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, - 0.0, Double.MIN_VALUE }; - gbl_configuration.rowWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE }; - configuration.setLayout(gbl_configuration); - - JLabel label1 = new JLabel("URL:"); - label1.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_label1 = new GridBagConstraints(); - gbc_label1.anchor = GridBagConstraints.EAST; - gbc_label1.insets = new Insets(0, 0, 5, 5); - gbc_label1.gridx = 0; - gbc_label1.gridy = 0; - configuration.add(label1, gbc_label1); - - JButton btnLoadXmlProfile = new JButton("Load JSAP"); - btnLoadXmlProfile.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - // String path = appProperties.getProperty("path"); - final JFileChooser fc = new JFileChooser(appProperties.getProperty("appProfile")); - // final JFileChooser fc = new - // JFileChooser("/Users/luca/Documents/SEPAProject/WOTDemo/tools/"); - DashboardFileFilter filter = new DashboardFileFilter("JSON SAP Profile (.jsap)", ".jsap"); - fc.setFileFilter(filter); - int returnVal = fc.showOpenDialog(frmSepaDashboard); - if (returnVal == JFileChooser.APPROVE_OPTION) { - String fileName = fc.getSelectedFile().getPath(); - - if (loadSAP(fileName)) { - - FileOutputStream out = null; - try { - out = new FileOutputStream("dashboard.properties"); - } catch (FileNotFoundException e3) { - logger.error(e3.getMessage()); - return; - } - - appProperties = new Properties(); - appProperties.put("appProfile", fileName); - - try { - appProperties.store(out, "Dashboard properties"); - } catch (IOException e1) { - logger.error(e1.getMessage()); - } - try { - out.close(); - } catch (IOException e2) { - logger.error(e2.getMessage()); - } - - } - } - } - }); - - labelUrl = new JLabel("---"); - GridBagConstraints gbc_labelUrl = new GridBagConstraints(); - gbc_labelUrl.anchor = GridBagConstraints.WEST; - gbc_labelUrl.insets = new Insets(0, 0, 5, 5); - gbc_labelUrl.gridx = 1; - gbc_labelUrl.gridy = 0; - configuration.add(labelUrl, gbc_labelUrl); - - JLabel label2 = new JLabel("http:"); - label2.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_label2 = new GridBagConstraints(); - gbc_label2.insets = new Insets(0, 0, 5, 5); - gbc_label2.gridx = 2; - gbc_label2.gridy = 0; - configuration.add(label2, gbc_label2); - - labelHttpPort = new JLabel("---"); - GridBagConstraints gbc_labelHttpPort = new GridBagConstraints(); - gbc_labelHttpPort.anchor = GridBagConstraints.WEST; - gbc_labelHttpPort.insets = new Insets(0, 0, 5, 5); - gbc_labelHttpPort.gridx = 3; - gbc_labelHttpPort.gridy = 0; - configuration.add(labelHttpPort, gbc_labelHttpPort); - - JLabel lblWs = new JLabel("ws:"); - lblWs.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_lblWs = new GridBagConstraints(); - gbc_lblWs.insets = new Insets(0, 0, 5, 5); - gbc_lblWs.gridx = 4; - gbc_lblWs.gridy = 0; - configuration.add(lblWs, gbc_lblWs); - - labelWsPort = new JLabel("---"); - GridBagConstraints gbc_labelWsPort = new GridBagConstraints(); - gbc_labelWsPort.anchor = GridBagConstraints.WEST; - gbc_labelWsPort.insets = new Insets(0, 0, 5, 5); - gbc_labelWsPort.gridx = 5; - gbc_labelWsPort.gridy = 0; - configuration.add(labelWsPort, gbc_labelWsPort); - - JLabel lblUpdate = new JLabel("update:"); - lblUpdate.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_lblUpdate = new GridBagConstraints(); - gbc_lblUpdate.insets = new Insets(0, 0, 5, 5); - gbc_lblUpdate.gridx = 6; - gbc_lblUpdate.gridy = 0; - configuration.add(lblUpdate, gbc_lblUpdate); - - labelUpdatePath = new JLabel("---"); - GridBagConstraints gbc_labelUpdatepath = new GridBagConstraints(); - gbc_labelUpdatepath.anchor = GridBagConstraints.WEST; - gbc_labelUpdatepath.insets = new Insets(0, 0, 5, 5); - gbc_labelUpdatepath.gridx = 7; - gbc_labelUpdatepath.gridy = 0; - configuration.add(labelUpdatePath, gbc_labelUpdatepath); - - JLabel lblQuery = new JLabel("query:"); - lblQuery.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_lblQuery = new GridBagConstraints(); - gbc_lblQuery.insets = new Insets(0, 0, 5, 5); - gbc_lblQuery.gridx = 8; - gbc_lblQuery.gridy = 0; - configuration.add(lblQuery, gbc_lblQuery); - - labelQueryPath = new JLabel("---"); - GridBagConstraints gbc_labelQueryPath = new GridBagConstraints(); - gbc_labelQueryPath.anchor = GridBagConstraints.WEST; - gbc_labelQueryPath.insets = new Insets(0, 0, 5, 5); - gbc_labelQueryPath.gridx = 9; - gbc_labelQueryPath.gridy = 0; - configuration.add(labelQueryPath, gbc_labelQueryPath); - - JLabel lblSubscribe = new JLabel("subscribe:"); - lblSubscribe.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_lblSubscribe = new GridBagConstraints(); - gbc_lblSubscribe.insets = new Insets(0, 0, 5, 5); - gbc_lblSubscribe.gridx = 10; - gbc_lblSubscribe.gridy = 0; - configuration.add(lblSubscribe, gbc_lblSubscribe); - - labelSubscribePath = new JLabel("---"); - GridBagConstraints gbc_labelSubscribePath = new GridBagConstraints(); - gbc_labelSubscribePath.anchor = GridBagConstraints.WEST; - gbc_labelSubscribePath.insets = new Insets(0, 0, 5, 5); - gbc_labelSubscribePath.gridx = 11; - gbc_labelSubscribePath.gridy = 0; - configuration.add(labelSubscribePath, gbc_labelSubscribePath); - GridBagConstraints gbc_btnLoadXmlProfile = new GridBagConstraints(); - gbc_btnLoadXmlProfile.insets = new Insets(0, 0, 5, 0); - gbc_btnLoadXmlProfile.anchor = GridBagConstraints.NORTHEAST; - gbc_btnLoadXmlProfile.gridx = 12; - gbc_btnLoadXmlProfile.gridy = 0; - configuration.add(btnLoadXmlProfile, gbc_btnLoadXmlProfile); - - JLabel label3 = new JLabel("https:"); - label3.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_label3 = new GridBagConstraints(); - gbc_label3.insets = new Insets(0, 0, 0, 5); - gbc_label3.gridx = 2; - gbc_label3.gridy = 1; - configuration.add(label3, gbc_label3); - - labelHttpsPort = new JLabel("---"); - GridBagConstraints gbc_labelHttpsPort = new GridBagConstraints(); - gbc_labelHttpsPort.anchor = GridBagConstraints.WEST; - gbc_labelHttpsPort.insets = new Insets(0, 0, 0, 5); - gbc_labelHttpsPort.gridx = 3; - gbc_labelHttpsPort.gridy = 1; - configuration.add(labelHttpsPort, gbc_labelHttpsPort); - - JLabel lblWss = new JLabel("wss:"); - lblWss.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_lblWss = new GridBagConstraints(); - gbc_lblWss.insets = new Insets(0, 0, 0, 5); - gbc_lblWss.gridx = 4; - gbc_lblWss.gridy = 1; - configuration.add(lblWss, gbc_lblWss); - - labelWssPort = new JLabel("---"); - GridBagConstraints gbc_labelWssPort = new GridBagConstraints(); - gbc_labelWssPort.anchor = GridBagConstraints.WEST; - gbc_labelWssPort.insets = new Insets(0, 0, 0, 5); - gbc_labelWssPort.gridx = 5; - gbc_labelWssPort.gridy = 1; - configuration.add(labelWssPort, gbc_labelWssPort); - - JLabel lblNewLabel = new JLabel("secure:"); - lblNewLabel.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - GridBagConstraints gbc_lblNewLabel = new GridBagConstraints(); - gbc_lblNewLabel.insets = new Insets(0, 0, 0, 5); - gbc_lblNewLabel.gridx = 6; - gbc_lblNewLabel.gridy = 1; - configuration.add(lblNewLabel, gbc_lblNewLabel); - - labelSecurePath = new JLabel("---"); - GridBagConstraints gbc_labelSecurePath = new GridBagConstraints(); - gbc_labelSecurePath.anchor = GridBagConstraints.WEST; - gbc_labelSecurePath.insets = new Insets(0, 0, 0, 5); - gbc_labelSecurePath.gridx = 7; - gbc_labelSecurePath.gridy = 1; - configuration.add(labelSecurePath, gbc_labelSecurePath); - JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP); GridBagConstraints gbc_tabbedPane = new GridBagConstraints(); gbc_tabbedPane.insets = new Insets(0, 0, 5, 0); gbc_tabbedPane.fill = GridBagConstraints.BOTH; gbc_tabbedPane.gridx = 0; - gbc_tabbedPane.gridy = 1; + gbc_tabbedPane.gridy = 0; frmSepaDashboard.getContentPane().add(tabbedPane, gbc_tabbedPane); JPanel primitives = new JPanel(); - tabbedPane.addTab("SPARQL 1.1 primitives", null, primitives, null); - primitives.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "SPARQL 1.1 Updates and Queries", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); + tabbedPane.addTab("SPARQL", null, primitives, null); + primitives.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null), "", + TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); GridBagLayout gbl_primitives = new GridBagLayout(); gbl_primitives.columnWidths = new int[] { 684, 0, 0 }; - gbl_primitives.rowHeights = new int[] { 114, 115, 0, 0, 0 }; + gbl_primitives.rowHeights = new int[] { 0, 114, 146, 0, 0, 0 }; gbl_primitives.columnWeights = new double[] { 1.0, 1.0, Double.MIN_VALUE }; - gbl_primitives.rowWeights = new double[] { 0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE }; + gbl_primitives.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE }; primitives.setLayout(gbl_primitives); + JPanel panel_1 = new JPanel(); + panel_1.setBorder(new LineBorder(new Color(0, 0, 0), 1, true)); + GridBagConstraints gbc_panel_1 = new GridBagConstraints(); + gbc_panel_1.insets = new Insets(0, 0, 5, 5); + gbc_panel_1.fill = GridBagConstraints.BOTH; + gbc_panel_1.gridx = 0; + gbc_panel_1.gridy = 0; + primitives.add(panel_1, gbc_panel_1); + GridBagLayout gbl_panel_1 = new GridBagLayout(); + gbl_panel_1.columnWidths = new int[] { 0, 0, 0 }; + gbl_panel_1.rowHeights = new int[] { 0, 22, 0, 0, 0 }; + gbl_panel_1.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; + gbl_panel_1.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; + panel_1.setLayout(gbl_panel_1); + + JLabel updateUrl = new JLabel("-"); + updateUrl.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_updateUrl = new GridBagConstraints(); + gbc_updateUrl.gridwidth = 2; + gbc_updateUrl.anchor = GridBagConstraints.WEST; + gbc_updateUrl.insets = new Insets(0, 0, 5, 0); + gbc_updateUrl.gridx = 0; + gbc_updateUrl.gridy = 0; + panel_1.add(updateUrl, gbc_updateUrl); + + JLabel lblUsinggraphuri = new JLabel(""); + GridBagConstraints gbc_lblUsinggraphuri = new GridBagConstraints(); + gbc_lblUsinggraphuri.insets = new Insets(0, 0, 5, 5); + gbc_lblUsinggraphuri.gridx = 0; + gbc_lblUsinggraphuri.gridy = 1; + panel_1.add(lblUsinggraphuri, gbc_lblUsinggraphuri); + + JLabel lblUsinggraphuri_1 = new JLabel("using-graph-uri:"); + GridBagConstraints gbc_lblUsinggraphuri_1 = new GridBagConstraints(); + gbc_lblUsinggraphuri_1.anchor = GridBagConstraints.EAST; + gbc_lblUsinggraphuri_1.insets = new Insets(0, 0, 5, 5); + gbc_lblUsinggraphuri_1.gridx = 0; + gbc_lblUsinggraphuri_1.gridy = 2; + panel_1.add(lblUsinggraphuri_1, gbc_lblUsinggraphuri_1); + + JLabel updateUsingGraphUri = new JLabel("-"); + updateUsingGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_updateUsingGraphUri = new GridBagConstraints(); + gbc_updateUsingGraphUri.anchor = GridBagConstraints.WEST; + gbc_updateUsingGraphUri.insets = new Insets(0, 0, 5, 0); + gbc_updateUsingGraphUri.gridx = 1; + gbc_updateUsingGraphUri.gridy = 2; + panel_1.add(updateUsingGraphUri, gbc_updateUsingGraphUri); + + JLabel lblNamedgraphuri = new JLabel("using-named-graph-uri:"); + GridBagConstraints gbc_lblNamedgraphuri = new GridBagConstraints(); + gbc_lblNamedgraphuri.anchor = GridBagConstraints.EAST; + gbc_lblNamedgraphuri.insets = new Insets(0, 0, 0, 5); + gbc_lblNamedgraphuri.gridx = 0; + gbc_lblNamedgraphuri.gridy = 3; + panel_1.add(lblNamedgraphuri, gbc_lblNamedgraphuri); + + JLabel updateNamedGraphUri = new JLabel("-"); + updateNamedGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_updateNamedGraphUri = new GridBagConstraints(); + gbc_updateNamedGraphUri.anchor = GridBagConstraints.WEST; + gbc_updateNamedGraphUri.gridx = 1; + gbc_updateNamedGraphUri.gridy = 3; + panel_1.add(updateNamedGraphUri, gbc_updateNamedGraphUri); + + JPanel panel_2 = new JPanel(); + panel_2.setBorder(new LineBorder(new Color(0, 0, 0), 1, true)); + GridBagConstraints gbc_panel_2 = new GridBagConstraints(); + gbc_panel_2.insets = new Insets(0, 0, 5, 0); + gbc_panel_2.fill = GridBagConstraints.BOTH; + gbc_panel_2.gridx = 1; + gbc_panel_2.gridy = 0; + primitives.add(panel_2, gbc_panel_2); + GridBagLayout gbl_panel_2 = new GridBagLayout(); + gbl_panel_2.columnWidths = new int[] { 0, 0, 0 }; + gbl_panel_2.rowHeights = new int[] { 0, 0, 0, 0, 0 }; + gbl_panel_2.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; + gbl_panel_2.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; + panel_2.setLayout(gbl_panel_2); + + JLabel queryUrl = new JLabel("-"); + queryUrl.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_queryUrl = new GridBagConstraints(); + gbc_queryUrl.gridwidth = 2; + gbc_queryUrl.anchor = GridBagConstraints.WEST; + gbc_queryUrl.insets = new Insets(0, 0, 5, 0); + gbc_queryUrl.gridx = 0; + gbc_queryUrl.gridy = 0; + panel_2.add(queryUrl, gbc_queryUrl); + + JLabel subscribeUrl = new JLabel("-"); + subscribeUrl.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_subscribeUrl = new GridBagConstraints(); + gbc_subscribeUrl.gridwidth = 2; + gbc_subscribeUrl.anchor = GridBagConstraints.WEST; + gbc_subscribeUrl.insets = new Insets(0, 0, 5, 5); + gbc_subscribeUrl.gridx = 0; + gbc_subscribeUrl.gridy = 1; + panel_2.add(subscribeUrl, gbc_subscribeUrl); + + JLabel lblDefaultgraphuri = new JLabel("default-graph-uri:"); + GridBagConstraints gbc_lblDefaultgraphuri = new GridBagConstraints(); + gbc_lblDefaultgraphuri.anchor = GridBagConstraints.EAST; + gbc_lblDefaultgraphuri.insets = new Insets(0, 0, 5, 5); + gbc_lblDefaultgraphuri.gridx = 0; + gbc_lblDefaultgraphuri.gridy = 2; + panel_2.add(lblDefaultgraphuri, gbc_lblDefaultgraphuri); + + JLabel queryDefaultGraphUri = new JLabel("-"); + queryDefaultGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_queryDefaultGraphUri = new GridBagConstraints(); + gbc_queryDefaultGraphUri.anchor = GridBagConstraints.WEST; + gbc_queryDefaultGraphUri.insets = new Insets(0, 0, 5, 0); + gbc_queryDefaultGraphUri.gridx = 1; + gbc_queryDefaultGraphUri.gridy = 2; + panel_2.add(queryDefaultGraphUri, gbc_queryDefaultGraphUri); + + JLabel lblNamedgraphuri_1 = new JLabel("named-graph-uri:"); + GridBagConstraints gbc_lblNamedgraphuri_1 = new GridBagConstraints(); + gbc_lblNamedgraphuri_1.anchor = GridBagConstraints.EAST; + gbc_lblNamedgraphuri_1.insets = new Insets(0, 0, 0, 5); + gbc_lblNamedgraphuri_1.gridx = 0; + gbc_lblNamedgraphuri_1.gridy = 3; + panel_2.add(lblNamedgraphuri_1, gbc_lblNamedgraphuri_1); + + JLabel queryNamedGraphUri = new JLabel("-"); + queryNamedGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); + GridBagConstraints gbc_queryNamedGraphUri = new GridBagConstraints(); + gbc_queryNamedGraphUri.anchor = GridBagConstraints.WEST; + gbc_queryNamedGraphUri.gridx = 1; + gbc_queryNamedGraphUri.gridy = 3; + panel_2.add(queryNamedGraphUri, gbc_queryNamedGraphUri); + JSplitPane splitPanel_Update = new JSplitPane(); splitPanel_Update.setResizeWeight(0.5); GridBagConstraints gbc_splitPanel_Update = new GridBagConstraints(); gbc_splitPanel_Update.insets = new Insets(0, 0, 5, 5); gbc_splitPanel_Update.fill = GridBagConstraints.BOTH; gbc_splitPanel_Update.gridx = 0; - gbc_splitPanel_Update.gridy = 0; + gbc_splitPanel_Update.gridy = 1; primitives.add(splitPanel_Update, gbc_splitPanel_Update); JPanel panel_4 = new JPanel(); @@ -1117,13 +1003,23 @@ public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { if (updatesList.getSelectedIndex() != -1) { - String sparql = appProfile.update(updatesList.getSelectedValue()); + String sparql = appProfile.getSPARQLUpdate(updatesList.getSelectedValue()); sparql = sparql.replaceFirst("\n", ""); sparql = sparql.replaceAll("\t", ""); sparql = sparql.trim(); SPARQLUpdate.setText(sparql); - Bindings bindings = appProfile.updateBindings(updatesList.getSelectedValue()); + String request = appProfile.getUpdateUrl(updatesList.getSelectedValue()); + if(appProfile.getUpdateMethod().equals(HTTPMethod.GET)) request = "GET " + request; + else if(appProfile.getUpdateMethod().equals(HTTPMethod.POST)) request = "POST " + request; + else if(appProfile.getUpdateMethod().equals(HTTPMethod.URL_ENCODED_POST)) request = "URL ENCODED POST " + request; + + updateUrl.setText(request); + + updateUsingGraphUri.setText(appProfile.getUsingGraphURI(updatesList.getSelectedValue())); + updateNamedGraphUri.setText(appProfile.getUsingNamedGraphURI(updatesList.getSelectedValue())); + + Bindings bindings = appProfile.getUpdateBindings(updatesList.getSelectedValue()); updateForcedBindingsDM.clearBindings(); if (bindings == null) return; @@ -1169,7 +1065,7 @@ public void valueChanged(ListSelectionEvent e) { gbc_splitPanel_Subscribe.insets = new Insets(0, 0, 5, 0); gbc_splitPanel_Subscribe.fill = GridBagConstraints.BOTH; gbc_splitPanel_Subscribe.gridx = 1; - gbc_splitPanel_Subscribe.gridy = 0; + gbc_splitPanel_Subscribe.gridy = 1; primitives.add(splitPanel_Subscribe, gbc_splitPanel_Subscribe); JPanel panel_6 = new JPanel(); @@ -1204,14 +1100,25 @@ public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { if (subscribesList.getSelectedIndex() != -1) { - String sparql = appProfile.subscribe(subscribesList.getSelectedValue()); + String sparql = appProfile.getSPARQLQuery(subscribesList.getSelectedValue()); sparql = sparql.replaceFirst("\n", ""); sparql = sparql.replaceAll("\t", ""); sparql = sparql.trim(); SPARQLSubscribe.setText(sparql); + + String request = appProfile.getQueryUrl(updatesList.getSelectedValue()); + if(appProfile.getQueryMethod().equals(HTTPMethod.GET)) request = "GET " + request; + else if(appProfile.getQueryMethod().equals(HTTPMethod.POST)) request = "POST " + request; + else if(appProfile.getQueryMethod().equals(HTTPMethod.URL_ENCODED_POST)) request = "URL ENCODED POST " + request; + + queryUrl.setText(request); + subscribeUrl.setText(appProfile.getSubscribeUrl(updatesList.getSelectedValue())); + + queryDefaultGraphUri.setText(appProfile.getDefaultGraphURI(subscribesList.getSelectedValue())); + queryNamedGraphUri.setText(appProfile.getNamedGraphURI(subscribesList.getSelectedValue())); } - Bindings bindings = appProfile.subscribeBindings(subscribesList.getSelectedValue()); + Bindings bindings = appProfile.getQueryBindings(subscribesList.getSelectedValue()); subscribeForcedBindingsDM.clearBindings(); if (bindings == null) return; @@ -1256,7 +1163,7 @@ public void valueChanged(ListSelectionEvent e) { gbc_scrollPane_Update.fill = GridBagConstraints.BOTH; gbc_scrollPane_Update.insets = new Insets(0, 0, 5, 5); gbc_scrollPane_Update.gridx = 0; - gbc_scrollPane_Update.gridy = 1; + gbc_scrollPane_Update.gridy = 2; primitives.add(scrollPane_Update, gbc_scrollPane_Update); SPARQLUpdate = new JTextArea(); @@ -1287,7 +1194,12 @@ public void actionPerformed(ActionEvent e) { String update = SPARQLUpdate.getText().replaceAll("[\n\t]", ""); long start = System.currentTimeMillis(); - Response result = sepaClient.update(update, forced); + Response result; + try { + result = sepaClient.update(updatesList.getSelectedValue(),update, forced,appProfile.getUsingGraphURI(updatesList.getSelectedValue()),appProfile.getUsingNamedGraphURI(updatesList.getSelectedValue())); + } catch (SEPAProtocolException | SEPASecurityException e1) { + result = new ErrorResponse(500,e1.getMessage()); + } long stop = System.currentTimeMillis(); String status = "DONE"; @@ -1304,7 +1216,7 @@ public void actionPerformed(ActionEvent e) { gbc_scrollPane_Subscribe.fill = GridBagConstraints.BOTH; gbc_scrollPane_Subscribe.insets = new Insets(0, 0, 5, 0); gbc_scrollPane_Subscribe.gridx = 1; - gbc_scrollPane_Subscribe.gridy = 1; + gbc_scrollPane_Subscribe.gridy = 2; primitives.add(scrollPane_Subscribe, gbc_scrollPane_Subscribe); SPARQLSubscribe = new JTextArea(); @@ -1313,7 +1225,7 @@ public void actionPerformed(ActionEvent e) { GridBagConstraints gbc_btnUpdate = new GridBagConstraints(); gbc_btnUpdate.insets = new Insets(0, 0, 5, 5); gbc_btnUpdate.gridx = 0; - gbc_btnUpdate.gridy = 2; + gbc_btnUpdate.gridy = 3; primitives.add(btnUpdate, gbc_btnUpdate); JPanel panel = new JPanel(); @@ -1321,7 +1233,7 @@ public void actionPerformed(ActionEvent e) { gbc_panel.insets = new Insets(0, 0, 5, 0); gbc_panel.fill = GridBagConstraints.BOTH; gbc_panel.gridx = 1; - gbc_panel.gridy = 2; + gbc_panel.gridy = 3; primitives.add(panel, gbc_panel); GridBagLayout gbl_panel = new GridBagLayout(); gbl_panel.columnWidths = new int[] { 0, 0, 0 }; @@ -1486,7 +1398,7 @@ public void actionPerformed(ActionEvent e) { gbc_bindingsResults.fill = GridBagConstraints.BOTH; gbc_bindingsResults.gridwidth = 2; gbc_bindingsResults.gridx = 0; - gbc_bindingsResults.gridy = 3; + gbc_bindingsResults.gridy = 4; primitives.add(bindingsResults, gbc_bindingsResults); bindingsResultsTable = new JTable(bindingsDM); @@ -1499,7 +1411,7 @@ public void actionPerformed(ActionEvent e) { bindingsResultsTable.setCellSelectionEnabled(true); subscriptions = new JTabbedPane(JTabbedPane.TOP); - tabbedPane.addTab("Subscriptions", null, subscriptions, null); + tabbedPane.addTab("Active subscriptions", null, subscriptions, null); JPanel namespaces = new JPanel(); tabbedPane.addTab("Namespaces", null, namespaces, null); @@ -1534,12 +1446,12 @@ public void mouseClicked(MouseEvent e) { gbc_infoPanel.anchor = GridBagConstraints.SOUTH; gbc_infoPanel.fill = GridBagConstraints.HORIZONTAL; gbc_infoPanel.gridx = 0; - gbc_infoPanel.gridy = 2; + gbc_infoPanel.gridy = 1; frmSepaDashboard.getContentPane().add(infoPanel, gbc_infoPanel); GridBagLayout gbl_infoPanel = new GridBagLayout(); - gbl_infoPanel.columnWidths = new int[] { 73, 0, 0, 97, 76, 0 }; + gbl_infoPanel.columnWidths = new int[] { 129, 73, 0, 0, 97, 76, 0 }; gbl_infoPanel.rowHeights = new int[] { 29, 0 }; - gbl_infoPanel.columnWeights = new double[] { 1.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; + gbl_infoPanel.columnWeights = new double[] { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; gbl_infoPanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE }; infoPanel.setLayout(gbl_infoPanel); @@ -1563,10 +1475,55 @@ public int getMaxCharactersPerLineCount() { dialog.setVisible(true); } }); + + JButton btnLoadXmlProfile = new JButton("Load JSAP"); + btnLoadXmlProfile.setBackground(SystemColor.textHighlight); + GridBagConstraints gbc_btnLoadXmlProfile = new GridBagConstraints(); + gbc_btnLoadXmlProfile.insets = new Insets(0, 0, 0, 5); + gbc_btnLoadXmlProfile.gridx = 0; + gbc_btnLoadXmlProfile.gridy = 0; + infoPanel.add(btnLoadXmlProfile, gbc_btnLoadXmlProfile); + btnLoadXmlProfile.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + + final JFileChooser fc = new JFileChooser(appProperties.getProperty("appProfile")); + DashboardFileFilter filter = new DashboardFileFilter("JSON SAP Profile (.jsap)", ".jsap"); + fc.setFileFilter(filter); + int returnVal = fc.showOpenDialog(frmSepaDashboard); + if (returnVal == JFileChooser.APPROVE_OPTION) { + String fileName = fc.getSelectedFile().getPath(); + + if (loadSAP(fileName)) { + FileOutputStream out = null; + try { + out = new FileOutputStream("dashboard.properties"); + } catch (FileNotFoundException e3) { + logger.error(e3.getMessage()); + return; + } + + appProperties = new Properties(); + appProperties.put("appProfile", fileName); + + try { + appProperties.store(out, "Dashboard properties"); + } catch (IOException e1) { + logger.error(e1.getMessage()); + } + try { + out.close(); + } catch (IOException e2) { + logger.error(e2.getMessage()); + } + + } + } + } + }); GridBagConstraints gbc_lblInfo = new GridBagConstraints(); gbc_lblInfo.anchor = GridBagConstraints.WEST; gbc_lblInfo.insets = new Insets(0, 10, 0, 5); - gbc_lblInfo.gridx = 0; + gbc_lblInfo.gridx = 1; gbc_lblInfo.gridy = 0; infoPanel.add(lblInfo, gbc_lblInfo); ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE); @@ -1578,7 +1535,7 @@ public void stateChanged(ChangeEvent e) { }); GridBagConstraints gbc_chckbxClearonnotify = new GridBagConstraints(); gbc_chckbxClearonnotify.insets = new Insets(0, 0, 0, 5); - gbc_chckbxClearonnotify.gridx = 1; + gbc_chckbxClearonnotify.gridx = 2; gbc_chckbxClearonnotify.gridy = 0; infoPanel.add(chckbxClearonnotify, gbc_chckbxClearonnotify); @@ -1596,7 +1553,7 @@ public void stateChanged(ChangeEvent e) { chckbxQname.setSelected(true); GridBagConstraints gbc_chckbxQname = new GridBagConstraints(); gbc_chckbxQname.insets = new Insets(0, 0, 0, 5); - gbc_chckbxQname.gridx = 2; + gbc_chckbxQname.gridx = 3; gbc_chckbxQname.gridy = 0; infoPanel.add(chckbxQname, gbc_chckbxQname); @@ -1604,7 +1561,7 @@ public void stateChanged(ChangeEvent e) { GridBagConstraints gbc_chckbxAutoscroll = new GridBagConstraints(); gbc_chckbxAutoscroll.anchor = GridBagConstraints.WEST; gbc_chckbxAutoscroll.insets = new Insets(0, 0, 0, 5); - gbc_chckbxAutoscroll.gridx = 3; + gbc_chckbxAutoscroll.gridx = 4; gbc_chckbxAutoscroll.gridy = 0; infoPanel.add(chckbxAutoscroll, gbc_chckbxAutoscroll); chckbxAutoscroll.setSelected(true); @@ -1612,7 +1569,7 @@ public void stateChanged(ChangeEvent e) { JButton btnClean = new JButton("Clear"); GridBagConstraints gbc_btnClean = new GridBagConstraints(); gbc_btnClean.anchor = GridBagConstraints.NORTHWEST; - gbc_btnClean.gridx = 4; + gbc_btnClean.gridx = 5; gbc_btnClean.gridy = 0; infoPanel.add(btnClean, gbc_btnClean); btnClean.addActionListener(new ActionListener() { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java index c4d2fdf1..80a08c1d 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java @@ -133,7 +133,7 @@ public boolean updateTest(String id, boolean secure) { notificationReceived = false; - String sparql = appProfile.update(id); + String sparql = appProfile.getSPARQLUpdate(id); if (!secure) System.out.println("UPDATE: " + sparql); @@ -152,7 +152,7 @@ public boolean updateTest(String id, boolean secure) { } public boolean queryTest(String id, int number, boolean secure) { - String sparql = appProfile.subscribe(id); + String sparql = appProfile.getSPARQLQuery(id); if (!secure) System.out.println("QUERY: " + sparql); @@ -177,7 +177,7 @@ public boolean queryTest(String id, int number, boolean secure) { } public boolean subscribeTest(String id, long results, boolean secure) { - String sparql = appProfile.subscribe(id); + String sparql = appProfile.getSPARQLQuery(id); if (secure) System.out.println("SECURE SUBSCRIBE: " + sparql); From 5f11a9d89c4c1ce05b49aacb551ea9938c8ed5ad Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 1 Jun 2018 08:31:42 +0200 Subject: [PATCH 07/76] Timeouts and refactory --- WebThings/defaults.jpar | 1 - WebThings/pom.xml | 85 --- .../apps/plugfest/ActionManager.java | 56 -- .../sepa/webthings/apps/plugfest/Context.java | 172 ----- .../sepa/webthings/apps/plugfest/Demo.java | 611 --------------- .../webthings/apps/plugfest/EventManager.java | 97 --- .../apps/plugfest/LemonbeatWater.java | 98 --- .../apps/plugfest/StatusMonitor.java | 118 --- .../webthings/apps/plugfest/TDPublisher.java | 79 -- .../apps/plugfest/ThingDiscover.java | 48 -- .../wot/sepa/webthings/mqtt/MQTTWebThing.java | 222 ------ .../rfidreader/CheckerWithHysteresis.java | 129 ---- .../rfidreader/ITagsChangesChecker.java | 8 - .../rfidreader/LabIdReaderTester.java | 108 --- .../rfidreader/RFIDEventListener.java | 54 -- .../webthings/rfidreader/RFIDWebThing.java | 160 ---- .../sepa/webthings/rfidreader/TagsReader.java | 204 ----- .../wot/sepa/webthings/rfidreader/td.jsap | 395 ---------- WebThings/src/main/resources/background.jpg | Bin 133059 -> 0 bytes WebThings/src/main/resources/no.png | Bin 140154 -> 0 bytes WebThings/src/main/resources/td.jsap | 394 ---------- WebThings/src/main/resources/yes.png | Bin 194362 -> 0 bytes .../src/test/java/org/WebThings/AppTest.java | 38 - .../wot/sepa/api/ISubscriptionHandler.java | 3 +- .../wot/sepa/api/ISubscriptionProtocol.java | 20 + .../wot/sepa/api/SPARQL11SEProperties.java | 11 +- .../wot/sepa/api/SPARQL11SEProtocol.java | 168 +---- .../websocket}/SEPAWebsocketClient.java | 5 +- .../websocket}/SPARQL11SESecureWebsocket.java | 6 +- .../websocket}/SPARQL11SEWebsocket.java | 35 +- .../WebSocketSubscriptionProtocol.java | 81 ++ .../commons/protocol/SPARQL11Properties.java | 20 +- .../commons/protocol/SPARQL11Protocol.java | 291 +++---- .../sepa/commons/request/QueryRequest.java | 45 +- .../wot/sepa/commons/request/Request.java | 51 +- .../commons/request/SubscribeRequest.java | 4 +- .../sepa/commons/request/UpdateRequest.java | 45 +- .../wot/sepa/commons/sparql/Bindings.java | 87 ++- .../wot/sepa/commons/sparql/RDFTerm.java | 8 +- .../sepa/commons/sparql/RDFTermLiteral.java | 56 +- .../wot/sepa/commons/sparql/RDFTermURI.java | 8 + .../arces/wot/sepa/pattern/Aggregator.java | 28 +- .../wot/sepa/pattern/ApplicationProfile.java | 150 ++-- .../unibo/arces/wot/sepa/pattern/Client.java | 13 +- .../arces/wot/sepa/pattern/Consumer.java | 64 +- .../arces/wot/sepa/pattern/GenericClient.java | 192 +++-- .../arces/wot/sepa/pattern/IConsumer.java | 3 +- .../arces/wot/sepa/pattern/IProducer.java | 4 +- .../arces/wot/sepa/pattern/Producer.java | 43 +- client-api/src/main/resources/log4j2.xml | 46 +- .../wot/sepa/api/ConfigurationProvider.java | 11 +- .../arces/wot/sepa/api/ITProtocolTest.java | 24 +- .../wot/sepa/api/ITSecureProtocolTest.java | 22 +- .../wot/sepa/api/MockSubscriptionHandler.java | 2 +- client-pac-pattern/LICENSE | 165 ---- client-pac-pattern/defaults.jpar | 1 - client-pac-pattern/pom.xml | 44 -- .../src/main/resources/defaults.jpar | 1 - .../src/main/resources/log4j2.xml | 17 - .../src/main/resources/sepa.jks | Bin 3103 -> 0 bytes .../src/test/resources/chat.jsap | 98 --- .../src/test/resources/habitat.jsap | 92 --- .../src/test/resources/log4j2.xml | 17 - client-wot-framework/pom.xml | 34 - .../arces/wot/framework/ThingDescription.java | 191 ----- .../wot/framework/discovery/Discoverable.java | 5 - .../wot/framework/discovery/Discovery.java | 105 --- .../arces/wot/framework/elements/Action.java | 38 - .../arces/wot/framework/elements/Context.java | 5 - .../arces/wot/framework/elements/Event.java | 81 -- .../wot/framework/elements/Property.java | 33 - .../arces/wot/framework/elements/Thing.java | 28 - .../framework/interaction/ActionListener.java | 85 --- .../interaction/ActionPublisher.java | 57 -- .../framework/interaction/EventListener.java | 170 ----- .../framework/interaction/EventPublisher.java | 64 -- .../it/unibo/arces/wot/wot_api/AppTest.java | 38 - commons/LICENSE | 674 ----------------- commons/pom.xml | 65 -- commons/src/main/resources/log4j2.xml | 17 - engine/endpoints/endpoint-stardog.jpar | 29 + .../wot/sepa/engine/bean/ProcessorBeans.java | 6 +- .../arces/wot/sepa/engine/core/Engine.java | 8 +- .../wot/sepa/engine/processing/Processor.java | 9 +- .../engine/processing/QueryProcessor.java | 42 +- .../engine/processing/UpdateProcessor.java | 35 +- .../engine/processing/subscriptions/SPU.java | 3 +- .../processing/subscriptions/SPUNaive.java | 8 +- .../protocol/http/handler/QueryHandler.java | 122 ++- .../protocol/http/handler/UpdateHandler.java | 19 +- .../test/java/engine/QueryProcessorTest.java | 2 +- .../test/java/engine/UpdateProcessorTest.java | 2 +- pom.xml | 4 - tools/GatewayProfile.jsap | 513 ------------- tools/bugs.jsap | 74 -- tools/chat.jsap | 175 ----- tools/client.jpar | 34 - tools/mqtt-SOSA-arces2.jsap | 578 -------------- tools/mqtt-SOSA.jsap | 195 ----- tools/mqtt.jsap | 626 --------------- tools/mqttAdapter.jsap | 160 ---- tools/mqttMonitor.jsap | 578 -------------- tools/randomNumbers.jsap | 74 -- tools/sepa.jks | Bin 2317 -> 0 bytes tools/sepatest-secure.jsap | 163 ---- tools/sepatest.jsap | 109 --- .../arces/wot/sepa/apps/HeapBugTest.java | 199 ----- .../arces/wot/sepa/apps/LCDProducer.java | 95 --- .../sepa/apps/RealTimeIoTResourceUpdate.java | 73 -- .../arces/wot/sepa/apps/chat/BasicClient.java | 74 -- .../wot/sepa/apps/chat/BasicHandler.java | 29 - .../arces/wot/sepa/apps/chat/ChatClient.java | 39 - .../arces/wot/sepa/apps/chat/DeleteAll.java | 18 - .../arces/wot/sepa/apps/chat/Message.java | 49 -- .../wot/sepa/apps/chat/PingPongClient.java | 21 - .../arces/wot/sepa/apps/chat/Receiver.java | 106 --- .../arces/wot/sepa/apps/chat/Remover.java | 92 --- .../wot/sepa/apps/chat/SEPAChatTest.java | 102 --- .../arces/wot/sepa/apps/chat/Sender.java | 42 -- .../wot/sepa/apps/chat/UpdateQueryTest.java | 207 ----- .../wot/sepa/apps/chat/UserRegistration.java | 22 - .../unibo/arces/wot/sepa/apps/chat/Users.java | 102 --- .../arces/wot/sepa/apps/mqtt/MQTTAdapter.java | 207 ----- .../wot/sepa/apps/mqtt/MQTTInitializer.java | 56 -- .../arces/wot/sepa/apps/mqtt/MQTTMonitor.java | 49 -- .../wot/sepa/apps/mqtt/MQTTSmartifier.java | 392 ---------- .../sepa/apps/mqtt/ObservationSimulator.java | 57 -- .../apps/randomnumbers/GarbageCollector.java | 88 --- .../apps/randomnumbers/MeanCalculator.java | 114 --- .../sepa/apps/randomnumbers/MeanMonitor.java | 78 -- .../randomnumbers/RandomNumberGenerator.java | 47 -- .../apps/randomnumbers/RandomNumbersTest.java | 64 -- .../apps/streetlamps/PopulateExperiment.java | 63 -- .../sepa/apps/streetlamps/RoadExperiment.java | 69 -- .../sepa/apps/streetlamps/RoadLightEx.java | 50 -- .../streetlamps/SmartLightingBenchmark.java | 499 ------------ .../unibo/arces/wot/sepa/tools/Dashboard.java | 547 ++++++++------ .../unibo/arces/wot/sepa/tools/Explorer.java | 224 ------ .../unibo/arces/wot/sepa/tools/SEPATest.java | 711 ------------------ .../arces/wot/sepa/tools/SEPATestClient.java | 397 ---------- tools/src/main/resources/log4j2.xml | 25 +- 141 files changed, 1449 insertions(+), 13874 deletions(-) delete mode 100644 WebThings/defaults.jpar delete mode 100644 WebThings/pom.xml delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ActionManager.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Context.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Demo.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/EventManager.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/LemonbeatWater.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/StatusMonitor.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/TDPublisher.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ThingDiscover.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/mqtt/MQTTWebThing.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/CheckerWithHysteresis.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/ITagsChangesChecker.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/LabIdReaderTester.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDEventListener.java delete mode 100755 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDWebThing.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/TagsReader.java delete mode 100644 WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/td.jsap delete mode 100644 WebThings/src/main/resources/background.jpg delete mode 100644 WebThings/src/main/resources/no.png delete mode 100644 WebThings/src/main/resources/td.jsap delete mode 100644 WebThings/src/main/resources/yes.png delete mode 100644 WebThings/src/test/java/org/WebThings/AppTest.java create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java rename client-api/src/main/java/it/unibo/arces/wot/sepa/api/{ => protocol/websocket}/SEPAWebsocketClient.java (95%) rename client-api/src/main/java/it/unibo/arces/wot/sepa/api/{ => protocol/websocket}/SPARQL11SESecureWebsocket.java (95%) rename client-api/src/main/java/it/unibo/arces/wot/sepa/api/{ => protocol/websocket}/SPARQL11SEWebsocket.java (75%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java delete mode 100644 client-pac-pattern/LICENSE delete mode 100644 client-pac-pattern/defaults.jpar delete mode 100644 client-pac-pattern/pom.xml delete mode 100644 client-pac-pattern/src/main/resources/defaults.jpar delete mode 100644 client-pac-pattern/src/main/resources/log4j2.xml delete mode 100644 client-pac-pattern/src/main/resources/sepa.jks delete mode 100644 client-pac-pattern/src/test/resources/chat.jsap delete mode 100644 client-pac-pattern/src/test/resources/habitat.jsap delete mode 100644 client-pac-pattern/src/test/resources/log4j2.xml delete mode 100644 client-wot-framework/pom.xml delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/ThingDescription.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discoverable.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discovery.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Action.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Context.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Event.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Property.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Thing.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionListener.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionPublisher.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventListener.java delete mode 100644 client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventPublisher.java delete mode 100644 client-wot-framework/src/test/java/it/unibo/arces/wot/wot_api/AppTest.java delete mode 100644 commons/LICENSE delete mode 100644 commons/pom.xml delete mode 100644 commons/src/main/resources/log4j2.xml create mode 100644 engine/endpoints/endpoint-stardog.jpar delete mode 100644 tools/GatewayProfile.jsap delete mode 100644 tools/bugs.jsap delete mode 100644 tools/chat.jsap delete mode 100644 tools/client.jpar delete mode 100644 tools/mqtt-SOSA-arces2.jsap delete mode 100644 tools/mqtt-SOSA.jsap delete mode 100644 tools/mqtt.jsap delete mode 100644 tools/mqttAdapter.jsap delete mode 100644 tools/mqttMonitor.jsap delete mode 100644 tools/randomNumbers.jsap delete mode 100644 tools/sepa.jks delete mode 100644 tools/sepatest-secure.jsap delete mode 100644 tools/sepatest.jsap delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/HeapBugTest.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/LCDProducer.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/RealTimeIoTResourceUpdate.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/DeleteAll.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Message.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UserRegistration.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/GarbageCollector.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanCalculator.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanMonitor.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumberGenerator.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumbersTest.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/PopulateExperiment.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadExperiment.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadLightEx.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/SmartLightingBenchmark.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/tools/Explorer.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATest.java delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java diff --git a/WebThings/defaults.jpar b/WebThings/defaults.jpar deleted file mode 100644 index b3f68a98..00000000 --- a/WebThings/defaults.jpar +++ /dev/null @@ -1 +0,0 @@ -{"parameters":{"host":"localhost","ports":{"http":9999,"https":8443,"ws":9000,"wss":9443},"paths":{"query":"/blazegraph/namespace/kb/sparql","update":"/blazegraph/namespace/kb/sparql","subscribe":"/subscribe","register":"/oauth/register","tokenRequest":"/oauth/token","securePath":"/secure"},"methods":{"query":"POST","update":"URL_ENCODED_POST"},"formats":{"query":"JSON","update":"HTML"}}} \ No newline at end of file diff --git a/WebThings/pom.xml b/WebThings/pom.xml deleted file mode 100644 index 1fd164d6..00000000 --- a/WebThings/pom.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - 4.0.0 - - it.unibo.arces.wot - sepa - ${revision} - - WebThings - WebThings - http://maven.apache.org - - UTF-8 - - - - Eclipse Paho Repo - https://repo.eclipse.org/content/repositories/paho-releases/ - - - - - - org.eclipse.paho - org.eclipse.paho.client.mqttv3 - 1.0.2 - - - - bouncycastle - bcprov-jdk16 - 140 - - - - org.bouncycastle - bcpkix-jdk15on - 1.57 - - - - junit - junit - 3.8.1 - test - - - org.apache.logging.log4j - log4j-api - 2.8.1 - compile - - - org.apache.logging.log4j - log4j-core - 2.8.1 - compile - - - com.google.code.gson - gson - 2.8.0 - compile - - - - org.apache.httpcomponents - httpclient - 4.5.2 - - - - org.apache.httpcomponents - httpcore - 4.4.6 - - - it.unibo.arces.wot - wot-api - 0.0.1-SNAPSHOT - - - diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ActionManager.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ActionManager.java deleted file mode 100644 index e02beafd..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ActionManager.java +++ /dev/null @@ -1,56 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.util.HashSet; -import java.util.Set; - -import it.unibo.arces.wot.framework.interaction.ActionPublisher; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.webthings.apps.plugfest.Context.COLOR; - -public class ActionManager { - // Actions - private static final String LCD_ACTION = "wot:LCDWriteAction"; - private static final String LED_COLOR_ACTION = "wot:ChangeColourAction"; - private static final String LED_FREQUENCY_ACTION = "wot:ChangeFrequencyAction"; - - private ActionPublisher actionOnLCD; - private ActionPublisher actionOnLEDColor; - private ActionPublisher actionOnLEDFrequency; - - public ActionManager() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - actionOnLCD = new ActionPublisher(LCD_ACTION); - actionOnLEDColor = new ActionPublisher(LED_COLOR_ACTION); - actionOnLEDFrequency = new ActionPublisher(LED_FREQUENCY_ACTION); - } - - public void setColors(Set colors) { - int r = 0, g = 0, b = 0; - for (COLOR c : colors) { - if (c.equals(COLOR.RED)) - r = 1; - else if (c.equals(COLOR.GREEN)) - g = 1; - else if (c.equals(COLOR.BLUE)) - b = 1; - } - String json = String.format("{\"r\":%d,\"g\":%d,\"b\":%d}", r, g, b); - actionOnLEDColor.post(json, "wot:ChangeRGBColorInputType"); - } - - public void setText(String message) { - actionOnLCD.post(message, "xsd:string"); - } - - public void setBlinking(boolean on) { - if (on) actionOnLEDFrequency.post("{\"frequency\":3}", "xsd:integer"); - else actionOnLEDFrequency.post("{\"frequency\":0}", "xsd:integer"); - } - - public void clearColors() { - HashSet empty = new HashSet(); - setColors(empty); - //setBlinking(false); - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Context.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Context.java deleted file mode 100644 index d9c7b182..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Context.java +++ /dev/null @@ -1,172 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map.Entry; -import java.util.Set; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; - -public class Context { - private CONTEXT_TYPE activeContextType; - - public enum CONTEXT_TYPE { - USERS, COLORS, CARDS - }; - - public enum COLOR { - RED, GREEN, BLUE - }; - - private HashMap colors = new HashMap(); - private HashMap cards = new HashMap(); - - // Users context - private HashMap users = new HashMap(); - private HashMap authorizations = new HashMap(); - private JsonObject db; - - public void setActiveContextType(CONTEXT_TYPE type) { - activeContextType = type; - } - - public CONTEXT_TYPE getActiveContextType() { - return activeContextType; - } - - public Context() throws FileNotFoundException { - colors.put("E0:02:22:0C:47:08:C2:C6", COLOR.RED); - colors.put("E0:02:22:0C:47:08:BA:57", COLOR.GREEN); - colors.put("E0:02:22:0C:47:08:9A:95", COLOR.BLUE); - - cards.put("E0:02:22:0C:47:08:BA:47", false); // Q - cards.put("E0:02:22:0C:47:08:BA:58", false); // J - cards.put("E0:02:22:0C:47:08:BA:48", true); // Jolly - -// addUserID("TAG1"); -// setUserName("TAG1","User1"); -// setUserAuthorization("TAG1",true); -// -// addUserID("TAG2"); -// setUserName("TAG2","User2"); -// setUserAuthorization("TAG2",false); - - loadUsers(); - } - - public COLOR getColor(String tag) { - return colors.get(tag); - } - - public boolean isJolly(String tag) { - return cards.get(tag); - } - - private void loadUsers() { - FileReader in = null; - - try { - in = new FileReader("usersDB.json"); - } catch (FileNotFoundException e) { - return; - } - if (in != null) { - try{ - db = new JsonParser().parse(in).getAsJsonObject(); - } - catch (JsonParseException | IllegalStateException e) { - return; - } - - for (Entry record : db.entrySet()) { - String id = record.getKey(); - String user = record.getValue().getAsJsonObject().get("user").getAsString(); - Boolean authorized = record.getValue().getAsJsonObject().get("authorized").getAsBoolean(); - users.put(id, user); - authorizations.put(id, authorized); - } - } - } - - private void storeUsers() throws IOException { - FileWriter out = new FileWriter("usersDB.json"); - db = new JsonObject(); - for (String uid : users.keySet()) { - JsonObject user = new JsonObject(); - user.add("user", new JsonPrimitive(users.get(uid))); - user.add("authorized", new JsonPrimitive(authorizations.get(uid))); - db.add(uid, user); - } - out.write(db.toString()); - out.close(); - } - - public boolean addUserID(String id){ - if (users.containsKey(id)) return false; - - users.put(id, ""); - authorizations.put(id, false); - - try { - storeUsers(); - } catch (IOException e) { - return false; - } - return true; - } - - public boolean setUserAuthorization(String id, Boolean authorized) { - if (users.get(id).equals("")) return false; - - authorizations.put(id, authorized); - try { - storeUsers(); - } catch (IOException e) { - return false; - } - return true; - } - - public boolean setUserName(String id, String name) { - users.put(id, name); - try { - storeUsers(); - } catch (IOException e) { - return false; - } - return true; - } - - public Set getAllUserIds() { - return users.keySet(); - } - - public String getUserName(String id) { - if (users.containsKey(id)) return users.get(id); - return ""; - } - - public boolean isAuthorized(String id) { - if (authorizations.containsKey(id)) return authorizations.get(id); - return false; - } - - public boolean isNewUser(String tag) { - return !users.containsKey(tag); - } - - public boolean isColor(String tag) { - return colors.containsKey(tag); - } - - public boolean isCard(String tag) { - return cards.containsKey(tag); - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Demo.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Demo.java deleted file mode 100644 index c4fac8fe..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/Demo.java +++ /dev/null @@ -1,611 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.awt.EventQueue; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.imageio.ImageIO; -import javax.swing.ImageIcon; -import javax.swing.JFrame; -import javax.swing.JTabbedPane; - -import javax.swing.JPanel; -import java.awt.GridBagLayout; -import java.awt.GridLayout; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URISyntaxException; -import java.security.InvalidKeyException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.NoSuchElementException; -import java.util.Set; -import java.awt.Color; -import javax.swing.JLabel; -import javax.swing.SwingConstants; -import java.awt.Font; - -import java.awt.GridBagConstraints; -import java.awt.Insets; -import javax.swing.border.EmptyBorder; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import javax.swing.event.TableModelListener; -import javax.swing.table.AbstractTableModel; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.webthings.apps.plugfest.Context.COLOR; -import it.unibo.arces.wot.sepa.webthings.apps.plugfest.Context.CONTEXT_TYPE; - -import javax.swing.JTable; -import javax.swing.JScrollPane; -import javax.swing.border.LineBorder; - -public class Demo { - protected static final Logger logger = LogManager.getLogger("WoTDemo"); - - private JFrame frmWebOfThings; - - private ImageIcon backgroundIcon; - private ImageIcon yesIcon; - private ImageIcon noIcon; - - // Cards - private JLabel backgroundLabel; - private JTable usersTable; - private JLabel chooseCardLabel; - - // Colors - private JPanel panelColorGreen; - private JPanel panelColorBlue; - private JPanel panelColorRed; - - // Users - private JLabel rfidLabel; - private UsersTableModel usersDM = new UsersTableModel(); - - // Event manager - private DemoEventManager eventManager; - - // Action manager - private ActionManager actionManager = new ActionManager(); - - // Context - Context context; - private JPanel infoPanel; - private JLabel infoLabel; - private JLabel onOffLabel; - private JPanel panelOnOff; - - private class DemoEventManager extends EventManager { - //Status monitor - private StatusMonitor monitor; - - public DemoEventManager(Context context) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - super(context); - - //Start the status monitor - monitor = new StatusMonitor(); - new Thread(monitor).start(); - } - - @Override - public void onPlayCards(boolean win) { - Set colors = new HashSet(); - - if (win) { - backgroundLabel.setIcon(yesIcon); - chooseCardLabel.setText("$$$ You won $$$"); - - actionManager.setText("$$$$$ YOU $$$$$$$$$$$ WON $$$$$$"); - colors.add(COLOR.GREEN); - actionManager.setColors(colors); - - } else { - backgroundLabel.setIcon(noIcon); - chooseCardLabel.setText("You lost"); - - actionManager.setText(":-( :-( :-( :-( YOU LOST "); - colors.add(COLOR.RED); - actionManager.setColors(colors); - - } - } - - @Override - public void onEmpty() { - actionManager.clearColors(); - - switch (context.getActiveContextType()) { - - case CARDS: - backgroundLabel.setIcon(backgroundIcon); - chooseCardLabel.setText("Choose your card"); - - actionManager.setText("Choose your card"); - break; - - case COLORS: - panelColorRed.setVisible(false); - panelColorGreen.setVisible(false); - panelColorBlue.setVisible(false); - - actionManager.setText("Play with colors"); - break; - - case USERS: - rfidLabel.setText("---"); - actionManager.setText("Pass a user TAG"); - break; - } - } - - @Override - public void onColors(Set colors) { - if (colors.contains(COLOR.RED)) - panelColorRed.setVisible(true); - else - panelColorRed.setVisible(false); - - if (colors.contains(COLOR.GREEN)) - panelColorGreen.setVisible(true); - else - panelColorGreen.setVisible(false); - - if (colors.contains(COLOR.BLUE)) - panelColorBlue.setVisible(true); - else - panelColorBlue.setVisible(false); - - actionManager.setColors(colors); - //actionManager.setBlinking(false); - } - - @Override - public void onReedEvent(boolean on) { - actionManager.setBlinking(on); - } - - @Override - public void onRFIDTags(String[] tags) { - Set color = new HashSet(); - color.add(COLOR.RED); - - actionManager.setColors(color); - actionManager.setText("Too many TAGS"); - } - - @Override - public void onRFIDTag(String tag) { - if (context.isColor(tag) || context.isCard(tag)) { - //Not allowed - Set color = new HashSet(); - color.add(COLOR.RED); - actionManager.setColors(color); - actionManager.setText("Wanna play with colors or cards?"); - return; - } - - if (context.isNewUser(tag)) { - // New user - context.addUserID(tag); - - usersDM.addUserID(tag); - rfidLabel.setText(tag); - actionManager.clearColors(); - - actionManager.setText(tag); - return; - } - - - Set color = new HashSet(); - String text = ""; - - if (context.isAuthorized(tag)) { - color.add(COLOR.GREEN); - text = context.getUserName(tag); - } else { - color.add(COLOR.RED); - text = tag; - } - - rfidLabel.setText(text); - - actionManager.setColors(color); - actionManager.setText(text); - } - - @Override - public void onConnectionStatus(Boolean on) { - if (on) { - onOffLabel.setText("ONLINE"); - panelOnOff.setBackground(Color.GREEN); - } - else { - onOffLabel.setText("OFFLINE"); - panelOnOff.setBackground(Color.RED); - } - - } - - @Override - public void onConnectionError(ErrorResponse error) { - // TODO Auto-generated method stub - - } - - } - - class UsersTableModel extends AbstractTableModel {// implements - // TableModelListener - // { - /** - * - */ - private static final long serialVersionUID = 2300692096939701619L; - - private HashMap rows = new HashMap(); - private HashMap records = new HashMap(); - private HashMap users = new HashMap(); - private HashMap authorized = new HashMap(); - - private HashMap columns = new HashMap(); - - public UsersTableModel() { - columns.put(0, "Authorized"); - columns.put(1, "RFID"); - columns.put(2, "User name"); - } - - public void addUserID(String id) { - if (records.containsKey(id)) - return; - - rows.put(getRowCount(), id); - records.put(id, getRowCount()); - - super.fireTableDataChanged(); - } - - public void setUserName(String id, String name) { - if (!records.containsKey(id)) - return; - - users.put(records.get(id), name); - - super.fireTableDataChanged(); - } - - public void setUserAuthorization(String id, Boolean auth) { - if (!records.containsKey(id)) - return; - - authorized.put(records.get(id), auth); - - super.fireTableDataChanged(); - } - - @Override - public int getRowCount() { - return records.size(); - } - - // Authorized RDFID Username - @Override - public int getColumnCount() { - return 3; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - switch (columnIndex) { - case 0: - return authorized.get(rowIndex); - case 1: - return rows.get(rowIndex); - case 2: - return users.get(rowIndex); - } - return null; - } - - @Override - public Class getColumnClass(int columnIndex) { - if (columnIndex == 1 || columnIndex == 2) - return String.class; - return Boolean.class; - } - - @Override - public String getColumnName(int columnIndex) { - if (columnIndex < getColumnCount()) - return columns.get(columnIndex); - return null; - } - - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - switch (columnIndex) { - case 0: - authorized.put(rowIndex, (Boolean) aValue); - context.setUserAuthorization((String) getValueAt(rowIndex,1), (Boolean) aValue); - break; - case 2: - users.put(rowIndex, (String) aValue); - context.setUserName((String) getValueAt(rowIndex,1), (String) aValue); - break; - } - } - - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - if (columnIndex == 1) - return false; - return true; - } - - @Override - public void addTableModelListener(TableModelListener l) { - super.addTableModelListener(l); - } - - @Override - public void removeTableModelListener(TableModelListener l) { - super.removeTableModelListener(l); - } - } - - /** - * Launch the application. - */ - public static void main(String[] args) { - EventQueue.invokeLater(new Runnable() { - public void run() { - try { - Demo window = new Demo(); - window.frmWebOfThings.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } - - /** - * Create the application. - * - * @throws URISyntaxException - * @throws IOException - * @throws BadPaddingException - * @throws IllegalBlockSizeException - * @throws NoSuchPaddingException - * @throws ClassCastException - * @throws NullPointerException - * @throws NoSuchElementException - * @throws FileNotFoundException - * @throws SEPAPropertiesException - * @throws SEPASecurityException - * @throws SEPAProtocolException - * @throws CertificateException - * @throws NoSuchAlgorithmException - * @throws KeyStoreException - * @throws IllegalArgumentException - * @throws InvalidKeyException - * @throws KeyManagementException - * @throws UnrecoverableKeyException - * @throws InterruptedException - */ - public Demo() throws FileNotFoundException, SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - - initialize(); - - context = new Context(); - context.setActiveContextType(CONTEXT_TYPE.COLORS); - - eventManager = new DemoEventManager(context); - eventManager.startListeningForEvents(); - - for (String id : context.getAllUserIds()) { - usersDM.addUserID(id); - usersDM.setUserName(id, context.getUserName(id)); - usersDM.setUserAuthorization(id, context.isAuthorized(id)); - } - - actionManager.clearColors(); - actionManager.setText("Play with colors"); - infoLabel.setText("WoT SEPA Demo - FRUCT 21 - Let Things Talk"); - } - - /** - * Initialize the contents of the frame. - */ - private void initialize() { - frmWebOfThings = new JFrame(); - frmWebOfThings.setResizable(false); - frmWebOfThings.setTitle("Web of Things Demo - FRUCT 21"); - frmWebOfThings.setBounds(0, 0, 1366, 700); - frmWebOfThings.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - GridBagLayout gridBagLayout = new GridBagLayout(); - gridBagLayout.columnWidths = new int[] { 640, 0 }; - gridBagLayout.rowHeights = new int[] { 720, 34, 0 }; - gridBagLayout.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gridBagLayout.rowWeights = new double[] { 1.0, 1.0, Double.MIN_VALUE }; - frmWebOfThings.getContentPane().setLayout(gridBagLayout); - - try { - backgroundIcon = new ImageIcon(ImageIO.read(new File("background.jpg"))); - } catch (IOException e) { - backgroundLabel.setText("background.jpg not found"); - } - - try { - yesIcon = new ImageIcon(ImageIO.read(new File("yes.png"))); - } catch (IOException e) { - backgroundLabel.setText("yes.png not found"); - } - - try { - noIcon = new ImageIcon(ImageIO.read(new File("no.png"))); - } catch (IOException e) { - backgroundLabel.setText("no.png not found"); - } - - JTabbedPane contextSelectPanel = new JTabbedPane(JTabbedPane.TOP); - GridBagConstraints gbc_contextSelectPanel = new GridBagConstraints(); - gbc_contextSelectPanel.insets = new Insets(0, 0, 5, 0); - gbc_contextSelectPanel.fill = GridBagConstraints.BOTH; - gbc_contextSelectPanel.gridx = 0; - gbc_contextSelectPanel.gridy = 0; - frmWebOfThings.getContentPane().add(contextSelectPanel, gbc_contextSelectPanel); - - JPanel panelRGBGame = new JPanel(); - contextSelectPanel.addTab("RGB Game", null, panelRGBGame, null); - panelRGBGame.setLayout(new GridLayout(0, 1, 0, 0)); - - panelColorRed = new JPanel(); - panelColorRed.setBackground(Color.RED); - panelRGBGame.add(panelColorRed); - - panelColorGreen = new JPanel(); - panelColorGreen.setBackground(Color.GREEN); - panelRGBGame.add(panelColorGreen); - - panelColorBlue = new JPanel(); - panelColorBlue.setBackground(Color.BLUE); - panelRGBGame.add(panelColorBlue); - - JPanel panelUsersID = new JPanel(); - contextSelectPanel.addTab("Users identification", null, panelUsersID, null); - GridBagLayout gbl_panelUsersID = new GridBagLayout(); - gbl_panelUsersID.columnWidths = new int[] { 0, 0 }; - gbl_panelUsersID.rowHeights = new int[] { 0, 0, 0 }; - gbl_panelUsersID.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gbl_panelUsersID.rowWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE }; - panelUsersID.setLayout(gbl_panelUsersID); - - rfidLabel = new JLabel("---"); - rfidLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 26)); - GridBagConstraints gbc_rfidLabel = new GridBagConstraints(); - gbc_rfidLabel.insets = new Insets(0, 0, 5, 0); - gbc_rfidLabel.gridx = 0; - gbc_rfidLabel.gridy = 0; - panelUsersID.add(rfidLabel, gbc_rfidLabel); - - JScrollPane scrollPane = new JScrollPane(); - GridBagConstraints gbc_scrollPane = new GridBagConstraints(); - gbc_scrollPane.fill = GridBagConstraints.BOTH; - gbc_scrollPane.gridx = 0; - gbc_scrollPane.gridy = 1; - panelUsersID.add(scrollPane, gbc_scrollPane); - - usersTable = new JTable(usersDM); - scrollPane.setViewportView(usersTable); - - JPanel panelCards = new JPanel(); - - panelCards.setBorder(new EmptyBorder(5, 5, 5, 5)); - contextSelectPanel.addTab("Three cards game", null, panelCards, null); - GridBagLayout gbl_panelCards = new GridBagLayout(); - gbl_panelCards.columnWidths = new int[] { 61, 0 }; - gbl_panelCards.rowHeights = new int[] { 16, 0, 0 }; - gbl_panelCards.columnWeights = new double[] { 1.0, Double.MIN_VALUE }; - gbl_panelCards.rowWeights = new double[] { 1.0, 0.0, Double.MIN_VALUE }; - panelCards.setLayout(gbl_panelCards); - - backgroundLabel = new JLabel(""); - GridBagConstraints gbc_backgroundLabel = new GridBagConstraints(); - gbc_backgroundLabel.insets = new Insets(0, 0, 5, 0); - gbc_backgroundLabel.anchor = GridBagConstraints.NORTH; - gbc_backgroundLabel.gridx = 0; - gbc_backgroundLabel.gridy = 0; - panelCards.add(backgroundLabel, gbc_backgroundLabel); - - chooseCardLabel = new JLabel("Choose your card"); - chooseCardLabel.setVerticalAlignment(SwingConstants.TOP); - chooseCardLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 60)); - GridBagConstraints gbc_chooseCardLabel = new GridBagConstraints(); - gbc_chooseCardLabel.gridx = 0; - gbc_chooseCardLabel.gridy = 1; - panelCards.add(chooseCardLabel, gbc_chooseCardLabel); - - backgroundLabel.setIcon(backgroundIcon); - - infoPanel = new JPanel(); - GridBagConstraints gbc_infoPanel = new GridBagConstraints(); - gbc_infoPanel.fill = GridBagConstraints.BOTH; - gbc_infoPanel.gridx = 0; - gbc_infoPanel.gridy = 1; - frmWebOfThings.getContentPane().add(infoPanel, gbc_infoPanel); - GridBagLayout gbl_infoPanel = new GridBagLayout(); - gbl_infoPanel.columnWidths = new int[] { 409, 723, 0, 0 }; - gbl_infoPanel.rowHeights = new int[] { 43, 0 }; - gbl_infoPanel.columnWeights = new double[] { 1.0, 1.0, 1.0, Double.MIN_VALUE }; - gbl_infoPanel.rowWeights = new double[] { 1.0, Double.MIN_VALUE }; - infoPanel.setLayout(gbl_infoPanel); - - infoLabel = new JLabel("Info"); - infoLabel.setFont(new Font("Lucida Grande", Font.BOLD, 15)); - GridBagConstraints gbc_infoLabel = new GridBagConstraints(); - gbc_infoLabel.insets = new Insets(0, 0, 0, 5); - gbc_infoLabel.gridx = 0; - gbc_infoLabel.gridy = 0; - infoPanel.add(infoLabel, gbc_infoLabel); - - panelOnOff = new JPanel(); - panelOnOff.setBorder(new LineBorder(new Color(0, 0, 0), 3)); - panelOnOff.setBackground(Color.RED); - GridBagConstraints gbc_panelOnOff = new GridBagConstraints(); - gbc_panelOnOff.fill = GridBagConstraints.BOTH; - gbc_panelOnOff.gridx = 2; - gbc_panelOnOff.gridy = 0; - infoPanel.add(panelOnOff, gbc_panelOnOff); - - onOffLabel = new JLabel("OFFLINE"); - onOffLabel.setHorizontalAlignment(SwingConstants.CENTER); - panelOnOff.add(onOffLabel); - onOffLabel.setForeground(Color.BLACK); - onOffLabel.setFont(new Font("Lucida Grande", Font.BOLD, 13)); - onOffLabel.setBackground(Color.BLACK); - - contextSelectPanel.addChangeListener(new ChangeListener() { - public void stateChanged(ChangeEvent e) { - System.out.println("Tab: " + contextSelectPanel.getSelectedIndex()); - actionManager.clearColors(); - switch (contextSelectPanel.getSelectedIndex()) { - case 0: - context.setActiveContextType(CONTEXT_TYPE.COLORS); - - actionManager.setText("Play with colors"); - break; - case 1: - context.setActiveContextType(CONTEXT_TYPE.USERS); - - actionManager.setText("Place a user TAG"); - break; - case 2: - context.setActiveContextType(CONTEXT_TYPE.CARDS); - - actionManager.setText("Choose your card"); - break; - } - } - }); - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/EventManager.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/EventManager.java deleted file mode 100644 index 88b41145..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/EventManager.java +++ /dev/null @@ -1,97 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.util.HashSet; -import java.util.Set; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.framework.elements.Event; -import it.unibo.arces.wot.framework.interaction.EventListener; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.webthings.apps.plugfest.Context.COLOR; - -public abstract class EventManager extends EventListener { - protected static final Logger logger = LogManager.getLogger("WoTDemoContextManager"); - - // Events - private static final String REED_EVENT = "wot:ReedSensorValueChangedEvent"; - private static final String RFID_EVENT = "wot:RFIDReading"; - - public EventManager(Context context) throws SEPAPropertiesException { - super(); - - if (context == null) - throw new IllegalArgumentException("Context is null"); - this.context = context; - - onEmpty(); - } - - public void startListeningForEvents() throws SEPAProtocolException, SEPASecurityException { - startListeningForEvent(RFID_EVENT); - startListeningForEvent(REED_EVENT); - } - - public abstract void onColors(Set colors); - - public abstract void onPlayCards(boolean win); - - public abstract void onEmpty(); - - public abstract void onReedEvent(boolean on); - - public abstract void onRFIDTags(String[] tags); - - public abstract void onRFIDTag(String tag); - - // Context reference - private Context context; - - @Override - public void onEvent(Set events) { - - for (Event event : events) { - logger.debug(event.getValue()); - - if (event.getEvent().equals(RFID_EVENT)) { - if (event.getValue().equals("EMPTY")) { - onEmpty(); - continue; - } - - String[] tags = event.getValue().split("\\|"); - - switch (context.getActiveContextType()) { - case USERS: - if (tags.length < 1) onRFIDTags(tags); - else onRFIDTag(tags[0]); - break; - case COLORS: - - - Set colors = new HashSet(); - for (String tag : tags) { - COLOR c = context.getColor(tag); - if (c != null) - colors.add(c); - } - onColors(colors); - break; - case CARDS: - if (context.isJolly(event.getValue())) { - onPlayCards(true); - } else { - onPlayCards(false); - } - break; - } - } - if (event.getEvent().equals(REED_EVENT)) { - onReedEvent(Boolean.parseBoolean(event.getValue())); - } - } - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/LemonbeatWater.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/LemonbeatWater.java deleted file mode 100644 index 371adc38..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/LemonbeatWater.java +++ /dev/null @@ -1,98 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Set; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.ParseException; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; - -import it.unibo.arces.wot.framework.elements.Event; -import it.unibo.arces.wot.framework.interaction.EventListener; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; - -public class LemonbeatWater extends EventListener { - /** The httpclient. */ - protected CloseableHttpClient httpclient = HttpClients.createDefault(); - - /** The response handler. */ - protected static ResponseHandler responseHandler; - - public LemonbeatWater() throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - super(); - this.startListeningForEvent("wot:RFIDReading"); - } - - public static void main(String[] args) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException, InterruptedException { - responseHandler = new ResponseHandler() { - @Override - public String handleResponse(final HttpResponse response) { - //Body - String body = null; - HttpEntity entity = response.getEntity(); - - try { - body = EntityUtils.toString(entity,Charset.forName("UTF-8")); - } catch (ParseException | IOException e) { - return e.getMessage(); - } - - return body; - } - }; - LemonbeatWater adapter = new LemonbeatWater(); - adapter.wait(); - } - - @Override - public void onEvent(Set events) { - for (Event event: events) { - if (!event.getEvent().equals("wot:RFIDReading")) continue; - - String[] tags = event.getValue().split("\\|"); - if (tags.length != 1) continue; - switch(tags[0]) { - case "E0:02:22:0C:47:08:C2:C6": - //RED close http://192.168.1.144:8080/water/turnoff - try { - httpclient.execute(new HttpPost("http://192.168.1.144:8080/water/turnoff")); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - break; - case "E0:02:22:0C:47:08:BA:57": - //GREEN open http://192.168.1.144:8080/water/turnon - try { - httpclient.execute(new HttpPost("http://192.168.1.144:8080/water/turnon")); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - break; - } - } - - } - - @Override - public void onConnectionStatus(Boolean on) { - - } - - @Override - public void onConnectionError(ErrorResponse error) { - - } - - -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/StatusMonitor.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/StatusMonitor.java deleted file mode 100644 index 1edf4060..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/StatusMonitor.java +++ /dev/null @@ -1,118 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.Aggregator; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class StatusMonitor extends Aggregator implements Runnable { - protected static final Logger logger = LogManager.getLogger("StatusMonitor"); - - private ConcurrentHashMap pings = new ConcurrentHashMap(); - private ConcurrentHashMap discoverables = new ConcurrentHashMap(); - - - public StatusMonitor() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("td.jsap"), "EVENT", "UPDATE_DISCOVER"); - - Bindings bindings = new Bindings(); - bindings.addBinding("event", new RDFTermURI("wot:Ping")); - subscribe(bindings); - } - - @Override - public void onResults(ARBindingsResults results) { - } - - @Override - public void onAddedResults(BindingsResults results) { - for (Bindings bindings : results.getBindings()) { - String thing = bindings.getBindingValue("thing"); - logger.info("Ping received by Web Thing: " + thing); - - synchronized (pings) { - pings.put(thing, true); - } - - if (discoverables.contains(thing)) - if (!discoverables.get(thing)) { - logger.info("Make Web Thing: " + thing + " discoverable again"); - switchStatus(thing, true); - } - } - } - - @Override - public void onRemovedResults(BindingsResults results) { - - } - - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - new Thread(new StatusMonitor()).start(); - } - - private void switchStatus(String thing, boolean status) { - // Update - Bindings bindings = new Bindings(); - if (status) - bindings.addBinding("value", new RDFTermLiteral("true")); - else - bindings.addBinding("value", new RDFTermLiteral("false")); - bindings.addBinding("thing", new RDFTermURI(thing)); - - Response ret = update(bindings); - - if (ret.isUpdateResponse()) { - discoverables.put(thing, status); - - if (status) - logger.warn("Web Thing: " + thing + " turned ON"); - else - logger.warn("Web Thing: " + thing + " turned OFF"); - } - } - - @Override - public void run() { - while (true) { - try { - Thread.sleep(6000); - logger.info("Check Web Things status...next check in 6 secs..."); - } catch (InterruptedException e) { - - } - - for (String thing : pings.keySet()) { - if (!pings.get(thing)) - switchStatus(thing, false); - pings.put(thing, false); - } - } - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/TDPublisher.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/TDPublisher.java deleted file mode 100644 index 444359d0..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/TDPublisher.java +++ /dev/null @@ -1,79 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import java.io.FileReader; -import java.io.IOException; -import java.util.Map.Entry; -//import java.util.UUID; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import it.unibo.arces.wot.framework.ThingDescription; - -public class TDPublisher { - JsonObject td; - - protected static final Logger logger = LogManager.getLogger("TDPublisher"); - - public TDPublisher(String tdFile) throws IOException { - - FileReader in = null; - - in = new FileReader(tdFile); - - if (in != null) { - td = new JsonParser().parse(in).getAsJsonObject(); - - for (Entry thing : td.entrySet()) { - try { - String thingURI = thing.getKey(); - String thingName = thing.getValue().getAsJsonObject().get("name").getAsString(); - - //String str = UUID.randomUUID().toString(); - ThingDescription thingDescription = new ThingDescription("", thingName); - - String thingBase = null; - if (thing.getValue().getAsJsonObject().get("base") != null) { - thingBase = thing.getValue().getAsJsonObject().get("base").getAsString(); - } - - JsonArray interaction = thing.getValue().getAsJsonObject().get("interaction").getAsJsonArray(); - - for(JsonElement actionProperty : interaction) { - String name = actionProperty.getAsJsonObject().get("name").getAsString(); - - JsonArray link = actionProperty.getAsJsonObject().get("link").getAsJsonArray(); - String href = link.get(0).getAsJsonObject().get("href").getAsString(); - if (thingBase != null) href = thingBase+href; - JsonArray type = actionProperty.getAsJsonObject().get("@type").getAsJsonArray(); - switch (type.get(0).getAsString()) { - case "Action": - thingDescription.addAction("", href, "wot:http"); - break; - case "Property": - thingDescription.addProperty("", name, "xsd:string", "-1", "true", href); - break; - } - } - } catch (Exception e) { - continue; - } - } - } - if (in != null) - in.close(); - } - - public static void main(String[] args) { - try { - new TDPublisher("plugfest-td.json"); - } catch (IOException e) { - logger.error(e); - } - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ThingDiscover.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ThingDiscover.java deleted file mode 100644 index 41b3118c..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/apps/plugfest/ThingDiscover.java +++ /dev/null @@ -1,48 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.apps.plugfest; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; - -public class ThingDiscover extends Consumer { - - public ThingDiscover() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { - super(new ApplicationProfile("td.jsap"), "ALL_THINGS"); - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onAddedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/mqtt/MQTTWebThing.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/mqtt/MQTTWebThing.java deleted file mode 100644 index 644e4458..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/mqtt/MQTTWebThing.java +++ /dev/null @@ -1,222 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.mqtt; - -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttClient; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import it.unibo.arces.wot.framework.ThingDescription; -import it.unibo.arces.wot.framework.interaction.EventPublisher; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SSLSecurityManager; - -import java.io.FileReader; -import java.io.IOException; - -import java.security.KeyManagementException; - -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; - -import java.security.UnrecoverableKeyException; - -import java.security.cert.CertificateException; - -import java.util.HashMap; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - - -public class MQTTWebThing implements MqttCallback { - private MqttClient mqttClient; - - private static final Logger logger = LogManager.getLogger("MQTTAdapter"); - - private boolean created = false; - - public static HashMap debugHash = new HashMap(); - private HashMap topicResponseCache = new HashMap(); - - private String serverURI = null; - private String[] topicsFilter = { "#" }; - private boolean sslEnabled = false; - private String clientID = "MQTTWebThing"; - - // WoT APIs - private EventPublisher event = null; - private ThingDescription webThing = null; - private String mqttEvent = "wot:mqttMessageReceived"; - - //private SSLSecurityManager sm = new SSLSecurityManager("TLSv1","sepa.jks","sepa2017","sepa2017"); - - public MQTTWebThing(String jsonFile) throws IOException, SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - - loadJSON(jsonFile); - - String thingURI = new String(serverURI); - thingURI = thingURI.replace(":", "_"); - thingURI = thingURI.replace("/", "_"); - thingURI = "wot:MQTTBroker_" + thingURI; - webThing = new ThingDescription(thingURI, clientID); - webThing.addEvent(mqttEvent, "MQTT Event"); - event = new EventPublisher(thingURI); - } - - private void loadJSON(String fileName) throws IOException { - FileReader in = null; - - in = new FileReader(fileName); - - if (in != null) { - JsonObject root = new JsonParser().parse(in).getAsJsonObject(); - - String url = root.get("url").getAsString(); - int port = root.get("port").getAsInt(); - JsonArray topics = root.get("topics").getAsJsonArray(); - - topicsFilter = new String[topics.size()]; - int i = 0; - for (JsonElement topic : topics) { - topicsFilter[i] = topic.getAsString(); - i++; - } - - if (root.get("ssl") != null) this.sslEnabled = root.get("ssl").getAsBoolean(); - else this.sslEnabled = false; - - if (sslEnabled){ - serverURI = "ssl://" + url + ":" + String.format("%d", port); - } else { - serverURI = "tcp://" + url + ":" + String.format("%d", port); - } - } - if (in != null) - in.close(); - } - - @Override - public void connectionLost(Throwable arg0) { - logger.error(arg0.getMessage()); - } - - @Override - public void deliveryComplete(IMqttDeliveryToken arg0) { - - } - - @Override - public void messageArrived(String topic, MqttMessage value) throws Exception { - logger.debug(topic + " " + value.toString()); - - event.post(mqttEvent, topic + "&" + value.toString()); - - topicResponseCache.put(topic, topic + "&" + value.toString()); - - if (debugHash.containsKey(topic)) { - if (!debugHash.get(topic).equals(value.toString())) { - logger.debug(topic + " " + debugHash.get(topic) + "-->" + value.toString()); - } - } - - debugHash.put(topic, value.toString()); - } - - public static void main(String[] args) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException, IOException { - - if (args.length != 1) { - logger.error("Please specify the configuration file (.json)"); - System.exit(1); - } - - MQTTWebThing adapter = new MQTTWebThing(args[0]); - - if (adapter.start()) { - logger.info("Press any key to exit..."); - System.in.read(); - adapter.stop(); - logger.info("Stopped"); - } else { - logger.fatal("NOT running"); - logger.info("Press any key to exit..."); - System.in.read(); - } - } - - public boolean start(){ - try { - mqttClient = new MqttClient(serverURI, clientID); - } catch (MqttException e) { - logger.fatal("Failed to create MQTT client " + e.getMessage()); - return created; - } - - try { - MqttConnectOptions options = new MqttConnectOptions(); - if (sslEnabled) { - SSLSecurityManager sm; - try { - sm = new SSLSecurityManager("TLSv1","sepa.jks","sepa2017","sepa2017"); - } catch (UnrecoverableKeyException | KeyManagementException | KeyStoreException - | NoSuchAlgorithmException | CertificateException | IOException e) { - logger.error(e.getMessage()); - return false; - } - try { - options.setSocketFactory(sm.getSSLContext().getSocketFactory()); - } catch (KeyManagementException | NoSuchAlgorithmException e) { - logger.error(e.getMessage()); - return false; - } - } - mqttClient.connect(options); - } catch (MqttException e) { - logger.fatal(e.getMessage()); - return created; - } - - mqttClient.setCallback(this); - - try { - mqttClient.subscribe(topicsFilter); - } catch (MqttException e) { - logger.fatal("Failed to subscribe " + e.getMessage()); - return created; - } - - String topics = ""; - for (int i = 0; i < topicsFilter.length; i++) - topics += "\"" + topicsFilter[i] + "\" "; - - logger.info("MQTT client " + clientID + " subscribed to " + serverURI + " Topic filter " + topics); - - created = true; - - return created; - } - - public void stop() { - try { - if (topicsFilter != null) - mqttClient.unsubscribe(topicsFilter); - } catch (MqttException e1) { - logger.error("Failed to unsubscribe " + e1.getMessage()); - } - - try { - mqttClient.disconnect(); - } catch (MqttException e) { - logger.error("Failed to disconnect " + e.getMessage()); - } - - } -} \ No newline at end of file diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/CheckerWithHysteresis.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/CheckerWithHysteresis.java deleted file mode 100644 index 1310396a..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/CheckerWithHysteresis.java +++ /dev/null @@ -1,129 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.rfidreader; - -import java.util.HashMap; -import java.util.HashSet; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class CheckerWithHysteresis implements ITagsChangesChecker { - private static final Logger logger = LogManager.getLogger("CheckerWithHysteresis"); - - private HashSet newTags = new HashSet(); - private HashSet oldTags = new HashSet(); - private HashSet activeTags = null; - - private int HIGH_TH = 6; - private int LOW_TH = 2; - private int MIN = 0; - private int MAX = 7; - private boolean enabled = false; - - private HashMap tagReadingsCount = new HashMap(); - - public CheckerWithHysteresis(boolean enabled) { - if (!enabled) { - HIGH_TH = 1; - LOW_TH = 1; - MIN = 0; - MAX = 1; - } - this.enabled = enabled; - } - - @Override - public HashSet checkChanges(HashSet tags) { - // Reset old and new tags - oldTags.clear(); - newTags.clear(); - - logger.debug("Input tags: "+tags); - - // Tags to be removed from counting tags set - HashSet toBeRemoved = new HashSet(); - - // Update tag counters - for (String tag : tagReadingsCount.keySet()) { - logger.debug(tag+" ? "+tagReadingsCount); - int count = tagReadingsCount.get(tag); - if (tags.contains(tag)) { - count++; - if (count > MAX) count = MAX; - tagReadingsCount.put(tag, count); - - // Overtake threshold? - if (count < HIGH_TH) - continue; - - // Is a new active tag? - if (!activeTags.contains(tag)) newTags.add(tag); - activeTags.add(tag); - } - else { - count--; - if (count < MIN) count = MIN; - tagReadingsCount.put(tag, count); - - // Overtake threshold? - if (count >= LOW_TH) - continue; - - // Is an old active tag? - if (!activeTags.contains(tag)) - continue; - - activeTags.remove(tag); - oldTags.add(tag); - - if (count == MIN) toBeRemoved.add(tag); - } - } - - // Remove tags with count == 0 - for (String tag: toBeRemoved){ - tagReadingsCount.remove(tag); - } - - // Insert new tags (count = 1) - for (String tag : tags) { - if (!tagReadingsCount.containsKey(tag)) tagReadingsCount.put(tag,1); - } - - if (activeTags == null) { - activeTags = new HashSet(); - newTags.add(""); - } - - logger.debug("Output tags: "+activeTags); - - return activeTags; - } - - public void setHighThreshold(int th) { - if (!enabled) return; - if (th < 0 ) HIGH_TH = 0; - else HIGH_TH = th; - } - - public void setLowThreshold(int th) { - if (!enabled) return; - if (th < 0 ) LOW_TH = 0; - else LOW_TH = th; - } - - public void setMin(int th){ - if (!enabled) return; - MIN = th; - } - - public void setMax(int th){ - if (!enabled) return; - MAX = th; - } - - @Override - public boolean isChanged() { - return !newTags.isEmpty() || !oldTags.isEmpty(); - } - -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/ITagsChangesChecker.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/ITagsChangesChecker.java deleted file mode 100644 index 2da7f01e..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/ITagsChangesChecker.java +++ /dev/null @@ -1,8 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.rfidreader; - -import java.util.HashSet; - -public interface ITagsChangesChecker { - public HashSet checkChanges(HashSet tags); - public boolean isChanged(); -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/LabIdReaderTester.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/LabIdReaderTester.java deleted file mode 100644 index 9c1138c2..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/LabIdReaderTester.java +++ /dev/null @@ -1,108 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.rfidreader; - -import java.io.IOException; -import java.util.HashSet; -import java.util.Scanner; - -import jssc.SerialPortList; -import labid.comm.ByteUtils; -import labid.iso15693.ISO15693Reader; -import labid.reader.RFReaderException; - -public class LabIdReaderTester { - private static ISO15693Reader reader; - - public static void main(String[] args) throws IOException, InterruptedException { - boolean searching = true; - String[] portNames = null; - int port = 0; - - Scanner in = new Scanner(System.in); - while (searching) { - portNames = SerialPortList.getPortNames(); - if (portNames.length == 0) { - System.out.println("No serial ports found...press CTRL+C to exit"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - in.close(); - return; - } - } else { - System.out.println("Choose one of the following serial ports: (0 no selection)"); - for (int i = 0; i < portNames.length; i++) { - System.out.printf("%d - %s\n", i + 1, portNames[i]); - } - - port = in.nextInt() - 1; - in.close(); - - if (port >= 0) { - System.out.printf("Selected port: %s\n", portNames[port]); - searching = false; - } - } - } - - // Create and configure the reader - reader = new ISO15693Reader();// stream); - reader.openSerialPort(portNames[port]); - - int N_READS = 10; - reader.rfReset(); - while (true) { - HashSet activeTags = new HashSet(); - - // reader.rfReset(); - //Thread.sleep(50); - - for (int t = 0; t < N_READS; t++) { - byte[][] uid = null; - Thread.sleep(100); - try { - uid = reader.inventory(); - } catch (RFReaderException e) { - System.out.println(e.getMessage()); - continue; - } -// if (uid == null) -// System.out.println(String.format("TAGS[%d]: 0", t)); - // Compose new UID list - if (uid != null) { - //System.out.println(String.format("TAGS[%d]: %d", t, uid.length)); - for (int i = 0; i < uid.length; i++) { - activeTags.add(uid[i]); - try { - reader.stayQuiet(uid[i]); - } catch (RFReaderException e) { - System.out.println(e.getMessage()); - } - } - } - } - - HashSet tagsPoll = new HashSet(); - for (byte[] tag : activeTags) { - tagsPoll.add(ByteUtils.toHexString(ByteUtils.revertedCopy(tag), ':')); - try { - reader.resetToReady(tag); - - } catch (RFReaderException e) { - System.out.println(e.getMessage()); - } - try { - reader.rfReset(); - } catch (RFReaderException e) { - System.out.println(e.getMessage()); - } - } - - // reader.rfOnOff(0); - // Thread.sleep(1000); - // reader.rfOnOff(1); - // reader.rfReset(); - - System.out.println(tagsPoll); - } - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDEventListener.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDEventListener.java deleted file mode 100644 index 8ac00676..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDEventListener.java +++ /dev/null @@ -1,54 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.rfidreader; - -import java.io.IOException; -import java.util.Set; - -import it.unibo.arces.wot.framework.elements.Event; -import it.unibo.arces.wot.framework.interaction.EventListener; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; - -public class RFIDEventListener extends EventListener { - - public RFIDEventListener() throws SEPAPropertiesException { - super(); - } - - public static void main(String[] args) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException, IOException { - - RFIDEventListener thing = new RFIDEventListener(); - - thing.startListeningForEvent("wot:Ping"); - thing.startListeningForEvent("wot:TagsPollChanged"); - //thing.startListeningForEvent("wot:RFIDReader", "wot:Ping"); - //thing.startListeningForEvent("wot:RFIDReader", "wot:TagsPollChanged"); - - System.out.println("Press x to exit..."); - - while(System.in.read()!='x'){} - - System.out.println("RFID Event Listener stopped");; - } - - @Override - public void onEvent(Set events) { - for (Event e: events) System.out.println(e); - - } - - @Override - public void onConnectionStatus(Boolean on) { - // TODO Auto-generated method stub - - } - - @Override - public void onConnectionError(ErrorResponse error) { - // TODO Auto-generated method stub - - } - - -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDWebThing.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDWebThing.java deleted file mode 100755 index 29bd97f8..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/RFIDWebThing.java +++ /dev/null @@ -1,160 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.rfidreader; - -import java.io.IOException; - -import java.util.HashSet; -import java.util.Scanner; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.framework.ThingDescription; -import it.unibo.arces.wot.framework.interaction.EventPublisher; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; -import jssc.SerialPortList; - -public class RFIDWebThing extends TagsReader { - private static final Logger logger = LogManager.getLogger("RFIDWebThing"); - - // WoT Framework - private ThingDescription td; - private EventPublisher event; - - // URIs and names - private final String pingEventURI = "wot:Ping"; - private final String pingEventName = "RFID Ping"; - private final String rfidReadingEvent = "wot:RFIDReading"; - private final String rfidReadingEventName = "RFID Reading"; - private final String thingURIPrefix = "wot:LABID_RFID_READER_"; - private final String thingName = "ARCES RFID UID:"; - - DiscoveryPatch patch = new DiscoveryPatch(); - - private CheckerWithHysteresis tagsChecker = new CheckerWithHysteresis(false); - - public RFIDWebThing(String port) - throws IOException, SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - super(port); - - // Publish the Thing Description - String thingURI = thingURIPrefix + getReaderUID(); - td = new ThingDescription(thingURI, thingName + getReaderUID()); - td.addEvent(rfidReadingEvent, rfidReadingEventName, "xsd:string"); - td.addEvent(pingEventURI, pingEventName); - - // Create the event generator - event = new EventPublisher(thingURI); - } - - public static void main(String[] args) throws IOException, SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - Scanner in = new Scanner(System.in); - boolean searching = true; - String portName = ""; - - System.out.println("Number of arguments: " + args.length); - - if (args.length == 1) { - portName = args[0]; - } else { - - while (searching) { - // 0 - /dev/tty.usbserial-000012FD - String[] portNames = SerialPortList.getPortNames(); - if (portNames.length == 0) { - System.out.println("No serial ports found...press CTRL+C to exit"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - in.close(); - return; - } - } else { - System.out.println("Choose one of the following serial ports: (0 no selection)"); - for (int i = 0; i < portNames.length; i++) { - System.out.printf("%d - %s\n", i + 1, portNames[i]); - } - - int port = in.nextInt() - 1; - - if (port >= 0) { - System.out.printf("Selected port: %s\n", portNames[port]); - searching = false; - portName = portNames[port]; - } - } - } - } - - // Web Thing adapter - RFIDWebThing adapter; - - adapter = new RFIDWebThing(portName); - - adapter.start(); - - System.out.println("RFID adapter is running..."); - - System.out.println("Press any key and hit return to exit"); - while (!in.hasNext()) { - } - in.close(); - - adapter.stop(); - - System.out.println("RFID adapter stopped"); - System.exit(0); - } - - class DiscoveryPatch extends Producer { - - public DiscoveryPatch() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("td.jsap"), "UPDATE_DISCOVER"); - } - - public void setDiscoverable(String thing) { - Bindings bindings = new Bindings(); - bindings.addBinding("thing", new RDFTermURI(thing)); - bindings.addBinding("value", new RDFTermLiteral("true")); - update(bindings); - } - - } - - @Override - public void onPing() { - logger.info("Event: " + pingEventURI); - event.post(pingEventURI); - - // PATCH for discovery - patch.setDiscoverable(thingURIPrefix); - } - - @Override - public void onTags(HashSet tags) { - - HashSet activeTags = tagsChecker.checkChanges(tags); - String tagsPoll = ""; - for (String tag : activeTags) { - if (tagsPoll.equals("")) - tagsPoll += tag; - else - tagsPoll += "|" + tag; - } - - if (tagsPoll.equals("")) - tagsPoll = "EMPTY"; - - if (tagsChecker.isChanged()) { - logger.info("Event: " + rfidReadingEvent); - event.post(rfidReadingEvent, tagsPoll); - } - } - -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/TagsReader.java b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/TagsReader.java deleted file mode 100644 index d2e910c5..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/TagsReader.java +++ /dev/null @@ -1,204 +0,0 @@ -package it.unibo.arces.wot.sepa.webthings.rfidreader; - -import java.io.IOException; - -import java.util.HashSet; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import labid.comm.ByteUtils; -import labid.comm.SerialStream; -import labid.iso15693.ISO15693Reader; -import labid.reader.RFReaderException; -import labid.reader.ReaderConfiguration; - -public abstract class TagsReader { - private static final Logger logger = LogManager.getLogger("TagsReader"); - - private final long PING_PERIOD = 5000; - private final int N_READS = 10; //10 - private final long DELAY = 10; //100 - - private static ISO15693Reader reader; - private ReaderConfiguration settings; - - private InventoryThread inventory = new InventoryThread(); - private boolean running = true; - private String uidStr = ""; - - public abstract void onPing(); - public abstract void onTags(HashSet tags); - - public TagsReader(String port) throws IOException { - // Open serial port - SerialStream stream = new SerialStream(); - stream.Open(port, 115200); - - // Create and configure the reader - reader = new ISO15693Reader(stream); - - reader.setDefaultConfiguration(); - settings = reader.getReaderConfiguration(); - - // Create the HW UID - byte[] UID = { 0, 0, 0, 0 }; - try { - UID = reader.getReaderUID(); - } catch (RFReaderException e) { - logger.warn(e.getMessage()); - } - - for (byte digit : UID) { - uidStr += String.format("%2X", digit); - } - } - - public String getReaderUID(){ - return uidStr; - } - - public void start() throws IOException { - running = true; - inventory.start(); - } - - public void stop() throws IOException { - running = false; - reader.close(); - } - - @SuppressWarnings("unused") - private void setReaderConfiguration() throws RFReaderException { - settings.AfiEnabled = false; - settings.AutoRfOff = false; - settings.Baudrate = 115; - - settings.BeepOnFailure = false; - settings.BeepOnSuccess = false; - - settings.DefaultProtocol = (byte) 0xB0; // 0xA0: ISO14443a 0xB0: - // ISO15693 - settings.DualSubcarrier = false; // FALSE!!! - settings.HighDataRate = true; // TRUE!!! - - settings.SecurityStatus = false; - settings.TI = false; // Texas Instrument tags only - settings.TimeSlot1 = false; - settings.VCDDataRate256 = true; - - settings.Scan_Enabled = false; // - settings.Scan_ISO14443A = false; // - - settings.Scan_AsciiOutput = false; // - settings.Scan_Fast = false; // - - settings.Scan_FirstBlock = 0; // ISO15693 - settings.Scan_IgnoreLast = false; // ISO15693 - settings.Scan_NBlocks = 0; // ISO15693 - settings.Scan_ReadDataBlocks = false; // ISO15693 - settings.Scan_ReadUid = false; // ISO15693 - settings.Scan_SingleRead = false; // ISO15693 - settings.Scan_WriteOk = false; // ISO15693 LIOK - - settings.MSB_first_ISO15693_DataBlocks = false; // ISO15693 - settings.MSB_first_ISO15693_UID = false; // ISO15693 - - reader.setReaderConfiguration(settings, false); - } - - class InventoryThread extends Thread { - - private boolean isAlive = false; - - private HashSet readTags(int nReads, long delay) throws InterruptedException, RFReaderException { - - HashSet activeTags = new HashSet(); - - for (int t = 0; t < nReads; t++) { - byte[][] uid = null; - Thread.sleep(delay); - try { - uid = reader.inventory(); - } catch (RFReaderException e) { - logger.warn(e.getMessage()); - continue; - } - - // Compose new UID list - if (uid != null) { - - for (int i = 0; i < uid.length; i++) { - activeTags.add(uid[i]); - try { - reader.stayQuiet(uid[i]); - } catch (RFReaderException e) { - logger.warn(e.getMessage()); - } - } - } - } - - isAlive = true; - - HashSet tagsPoll = new HashSet(); - for (byte[] tag : activeTags) { - tagsPoll.add(ByteUtils.toHexString(ByteUtils.revertedCopy(tag), ':')); - try { - reader.resetToReady(tag); - - } catch (RFReaderException e) { - logger.warn(e.getMessage()); - } - try { - reader.rfReset(); - } catch (RFReaderException e) { - logger.warn(e.getMessage()); - } - } - - return tagsPoll; - } - - public void run() { - new Thread() { - @Override - public void run() { - while (running) { - try { - Thread.sleep(PING_PERIOD); - } catch (InterruptedException e) { - logger.debug("Ping thread exit!"); - return; - } - - if (isAlive) { - isAlive = false; - onPing(); - } - } - } - }.start(); - - try { - reader.rfReset(); - } catch (RFReaderException e1) { - logger.error(e1.getMessage()); - } - - while (running) { - // Read tags - HashSet current = null; - try { - current = readTags(N_READS, DELAY); - } catch (RFReaderException | InterruptedException e) { - return; - } - - // Notify tags - onTags(current); - } - } - - } -} diff --git a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/td.jsap b/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/td.jsap deleted file mode 100644 index 40895018..00000000 --- a/WebThings/src/main/java/it/unibo/arces/wot/sepa/webthings/rfidreader/td.jsap +++ /dev/null @@ -1,395 +0,0 @@ -{ - "parameters" : { - "host" : "10.10.10.100" , - "ports" : { - "http" : 8000 , - "https" : 8443 , - "ws" : 9000 , - "wss" : 9443} - , - "paths" : { - "query" : "/query" , - "update" : "/update" , - "subscribe" : "/subscribe" , - "register" : "/oauth/register" , - "tokenRequest" : "/oauth/token" , - "securePath" : "/secure"} - , - "concurrentRequests" : 5 , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} - } - , - "namespaces" : { - "wot" : "http://wot.arces.unibo.it/sepa#" , - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "dul" : "http://www.ontologydesignpatterns.org/ont/dul/DUL.owl#" , - "ire" : "http://w3c.github.io/wot/w3c-wot-td-ire.owl#" , - "rdfs" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "td" : "http://www.w3.org/ns/td#" , - "xsd" : "http://www.w3.org/2001/XMLSchema"} - , - "updates" : { - "TD_INIT" : { - "sparql" : "DELETE {?thing wot:isDiscoverable ?discoverable . ?thing td:hasName ?oldName . ?thing wot:hasComponent ?component. ?component rdf:type td:Thing . ?thing td:hasProperty ?property. ?property td:hasName ?pName. ?property td:hasStability ?pStability. ?property td:isWritable ?pWrite. ?pValueType rdf:type ?pDataType . ?pValueType dul:hasDataValue ?pDataValue . ?thing td:hasEvent ?event. ?event td:hasName ?eName. ?event td:forProperty ?eProperty . ?thing td:hasAction ?action. ?action td:hasName ?aName. ?action wot:isAccessibleBy ?aProtocol. ?action td:forProperty ?aProperty} INSERT {?thing rdf:type td:Thing . ?thing td:hasName ?name. ?thing wot:isDiscoverable 'true'} WHERE { OPTIONAL {?thing rdf:type td:Thing. ?thing wot:isDiscoverable ?discoverable . ?thing td:hasName ?oldName} . OPTIONAL {?thing wot:hasComponent ?component. ?component rdf:type td:Thing} . OPTIONAL {?thing td:hasProperty ?property. ?property td:hasName ?pName. ?property td:hasStability ?pStability. ?property td:isWritable ?pWrite. ?pValueType rdf:type ?pDataType . ?pValueType dul:hasDataValue ?pDataValue} . OPTIONAL {?thing td:hasEvent ?event. ?event td:hasName ?eName. OPTIONAL {?event td:forProperty ?eProperty}} . OPTIONAL {?thing td:hasAction ?action. ?action td:hasName ?aName. ?action wot:isAccessibleBy ?aProtocol. OPTIONAL {?action td:forProperty ?aProperty}} }" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - } - } - , - "TD_ADD_PROPERTY" : { - "sparql" : " DELETE { ?thing td:hasProperty ?property . ?property rdf:type td:Property . ?property td:hasStability ?oldStability. ?property dul:hasDataValue ?oldValue . ?property td:hasName ?oldName . ?property td:isWritable ?oldWritable . ?property td:hasValueType ?oldDataType } INSERT {?thing td:hasProperty ?property . ?property rdf:type td:Property . ?property td:hasName ?name. ?property td:hasStability ?stability. ?property td:isWritable ?writable. ?property td:hasValueType ?dataType . ?property dul:hasDataValue ?value} WHERE {?thing rdf:type td:Thing . OPTIONAL { ?thing td:hasProperty ?property . ?property rdf:type td:Property . ?property td:hasStability ?oldStability. ?property dul:hasDataValue ?oldValue . ?property td:hasName ?oldName . ?property td:isWritable ?oldWritable . ?property td:hasValueType ?oldDataType}}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "dataType" : { - "type" : "uri" , - "value" : ""} - , - "property" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - , - "stability" : { - "type" : "literal" , - "value" : ""} - , - "writable" : { - "type" : "literal" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - , - "TD_ADD_ACTION" : { - "sparql" : " DELETE {?thing td:hasAction ?action. ?action rdf:type td:Action. ?action td:hasName ?oldName . ?action wot:isAccessibleBy ?protocol} INSERT {?thing td:hasAction ?action. ?action rdf:type td:Action. ?action td:hasName ?name . ?action wot:isAccessibleBy ?protocol} WHERE {?thing rdf:type td:Thing . OPTIONAL {?thing td:hasAction ?action. ?action rdf:type td:Action. ?action td:hasName ?oldName . ?action wot:isAccessibleBy ?protocol}}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "action" : { - "type" : "uri" , - "value" : ""} - , - "protocol" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - } - } - , - "TD_ADD_ACTION_WITH_INPUT" : { - "sparql" : "DELETE {?thing td:hasAction ?action. ?action rdf:type td:Action. ?action td:hasName ?oldName. ?action td:hasInput ?oldInput . ?oldInput rdf:type wot:ActionInput . ?oldInput td:hasDataType ?oldDataType . ?action wot:isAccessibleBy ?protocol} INSERT {?thing td:hasAction ?action. ?action rdf:type td:Action. ?action td:hasName ?name. ?action td:hasInput ?input . ?input rdf:type wot:ActionInput . ?input td:hasDataType ?dataType . ?action wot:isAccessibleBy ?protocol} WHERE { ?thing rdf:type td:Thing . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Input_',STRUUID())) AS ?input) . OPTIONAL {?thing td:hasAction ?action. ?action rdf:type td:Action. ?action td:hasName ?oldName. ?action td:hasInput ?oldInput . ?oldInput rdf:type wot:ActionInput . ?oldInput td:hasDataType ?oldDataType . ?action wot:isAccessibleBy ?protocol}}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "action" : { - "type" : "uri" , - "value" : ""} - , - "dataType" : { - "type" : "uri" , - "value" : ""} - , - "protocol" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - } - } - , - "TD_ADD_EVENT" : { - "sparql" : " DELETE {?event td:hasName ?oldName} INSERT {?thing td:hasEvent ?event. ?event rdf:type td:Event. ?event td:hasName ?name } WHERE {?thing rdf:type td:Thing . OPTIONAL{?thing td:hasEvent ?event. ?event rdf:type td:Event. ?event td:hasName ?oldName}}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "event" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - } - } - , - "TD_ADD_EVENT_WITH_OUTPUT" : { - "sparql" : " DELETE {?thing td:hasEvent ?event. ?event rdf:type td:Event. ?event td:hasName ?oldName . ?event td:hasDataType ?oldDataType } INSERT {?thing td:hasEvent ?event. ?event rdf:type td:Event. ?event td:hasName ?name . ?event td:hasDataType ?dataType } WHERE {?thing rdf:type td:Thing . OPTIONAL{?thing td:hasEvent ?event. ?event rdf:type td:Event. ?event td:hasName ?oldName . ?event td:hasDataType ?oldDataType }}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "event" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - , - "dataType" : { - "type" : "uri" , - "value" : ""} - } - } - , - "TD_ADD_PROPERTY_CHANGED_EVENT" : { - "sparql" : " DELETE {?thing td:hasEvent ?event. ?event rdf:type td:PropertyChangedEvent. ?event td:hasName ?oldName . ?event td:hasDataType ?oldDataType } INSERT {?thing td:hasEvent ?event. ?event rdf:type td:Event . ?event rdf:type td:PropertyChangedEvent. ?event td:hasName ?name . ?event td:hasDataType ?dataType } WHERE {?thing rdf:type td:Thing . OPTIONAL{?thing td:hasEvent ?event. ?event rdf:type td:PropertyChangedEvent. ?event td:hasName ?oldName . ?event td:hasDataType ?oldDataType }}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "event" : { - "type" : "uri" , - "value" : ""} - , - "name" : { - "type" : "literal" , - "value" : ""} - , - "dataType" : { - "type" : "uri" , - "value" : ""} - } - } - , - "TD_APPEND_ACCESS_PROTOCOL_TO_ACTION" : { - "sparql" : " INSERT {?action wot:isAccessibleBy ?protocol} WHERE {?action rdf:type td:Action. ?protocol rdf:type wot:Protocol}" , - "forcedBindings" : { - "action" : { - "type" : "uri" , - "value" : ""} - , - "protocol" : { - "type" : "uri" , - "value" : ""} - } - } - , - "TD_APPEND_TARGET_PROPERTY_TO_ACTION_OR_EVENT" : { - "sparql" : "INSERT {?action_OR_event td:forProperty ?property} WHERE { {{?action_OR_event rdf:type td:Action} UNION {?action_OR_event rdf:type td:Event}}. ?property rdf:type td:Property}" , - "forcedBindings" : { - "action_OR_event" : { - "type" : "uri" , - "value" : ""} - , - "property" : { - "type" : "uri" , - "value" : ""} - } - } - , - "POST_EVENT_WITH_OUTPUT" : { - "sparql" : " DELETE { ?event wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:EventInstance. ?oldInstance wot:isGeneratedBy ?thing . ?oldInstance wot:hasTimeStamp ?eOldTimeStamp. ?oldInstance td:hasOutput ?eOldOutput. ?eOldOutput dul:hasDataValue ?oldValue} INSERT { ?event wot:hasInstance ?newInstance. ?newInstance rdf:type wot:EventInstance. ?newInstance wot:isGeneratedBy ?thing . ?newInstance wot:hasTimeStamp ?time . ?newInstance td:hasOutput ?eNewOutput. ?eNewOutput dul:hasDataValue ?value} WHERE {?event rdf:type td:Event . BIND(now() AS ?time) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Event_',STRUUID())) AS ?newInstance) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Output_',STRUUID())) AS ?eNewOutput) . OPTIONAL {?event wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:EventInstance. ?oldInstance wot:isGeneratedBy ?thing . ?oldInstance wot:hasTimeStamp ?eOldTimeStamp. ?oldInstance td:hasOutput ?eOldOutput. ?eOldOutput dul:hasDataValue ?oldValue}}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "event" : { - "type" : "uri" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - , - "POST_EVENT_FOR_PROPERTY_CHANGED" : { - "sparql" : " DELETE { ?event wot:hasInstance ?oldInstance . ?oldInstance rdf:type wot:EventInstance . ?oldInstance wot:isGeneratedBy ?thing . ?oldInstance wot:hasTimeStamp ?eOldTimeStamp . ?oldInstance td:hasOutput ?eOldOutput . ?eOldOutput dul:hasDataValue ?oldValue . ?property dul:hasValueType ?oldValue } INSERT { ?event wot:hasInstance ?newInstance . ?newInstance rdf:type wot:EventInstance . ?newInstance wot:isGeneratedBy ?thing . ?newInstance wot:hasTimeStamp ?time . ?newInstance td:hasOutput ?eNewOutput . ?eNewOutput dul:hasDataValue ?value . ?property dul:hasDataValue ?value } WHERE { ?event rdf:type td:Event . ?event td:forProperty ?property . ?property rdf:type td:Property . BIND(now() AS ?time) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Event_',STRUUID())) AS ?newInstance) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Output_',STRUUID())) AS ?eNewOutput) . OPTIONAL { ?event wot:hasInstance ?oldInstance . ?oldInstance rdf:type wot:EventInstance . ?oldInstance wot:isGeneratedBy ?thing . ?oldInstance wot:hasTimeStamp ?eOldTimeStamp . ?oldInstance td:hasOutput ?eOldOutput . ?eOldOutput dul:hasDataValue ?oldValue ?property dul:hasDataValue ?oldValue }}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - , - "event" : { - "type" : "uri" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - , - "POST_EVENT" : { - "sparql" : " DELETE { ?event wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:EventInstance. ?oldInstance wot:isGeneratedBy ?thing . ?oldInstance wot:hasTimeStamp ?eOldTimeStamp} INSERT {?event wot:hasInstance ?newInstance. ?newInstance wot:isGeneratedBy ?thing . ?newInstance rdf:type wot:EventInstance. ?newInstance wot:hasTimeStamp ?time} WHERE { ?event rdf:type td:Event. BIND(now() AS ?time) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Event_',STRUUID())) AS ?newInstance) . OPTIONAL {?event wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:EventInstance. ?oldInstance wot:isGeneratedBy ?thing . ?oldInstance wot:hasTimeStamp ?eOldTimeStamp}}" , - "forcedBindings" : { - "event" : { - "type" : "uri" , - "value" : ""} - , - "thing" : { - "type" : "uri" , - "value" : ""} - } - } - , - "POST_ACTION_WITH_INPUT" : { - "sparql" : " DELETE {?action wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:ActionInstance. ?oldInstance wot:hasTimeStamp ?aOldTimeStamp. ?oldInstance td:hasInput ?oldInput. ?oldInput dul:hasDataValue ?oldValue} INSERT {?action wot:hasInstance ?newInstance. ?newInstance rdf:type wot:ActionInstance. ?newInstance wot:hasTimeStamp ?time. ?newInstance td:hasInput ?newInput. ?newInput dul:hasDataValue ?value} WHERE {?action rdf:type td:Action . ?thing td:hasAction ?action . ?thing wot:isDiscoverable 'true' . BIND(now() AS ?time) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#Action_',STRUUID())) AS ?newInstance) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#ActionInput_',STRUUID())) AS ?newInput) . OPTIONAL{?action wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:ActionInstance. ?oldInstance wot:hasTimeStamp ?aOldTimeStamp. ?oldInstance td:hasInput ?oldInput. ?oldInput dul:hasDataValue ?oldValue}}" , - "forcedBindings" : { - "action" : { - "type" : "uri" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - , - "POST_ACTION" : { - "sparql" : " DELETE {?action wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:ActionInstance. ?oldInstance wot:hasTimeStamp ?aOldTimeStamp} INSERT {?action wot:hasInstance ?newInstance. ?newInstance rdf:type wot:ActionInstance. ?newInstance wot:hasTimeStamp ?time} WHERE {?action rdf:type td:Action . ?thing td:hasAction ?action . ?thing wot:isDiscoverable 'true' . BIND(now() AS ?time) . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#ActionInstance_',STRUUID())) AS ?newInstance) . OPTIONAL{?action wot:hasInstance ?oldInstance. ?oldInstance rdf:type wot:ActionInstance. ?oldInstance wot:hasTimeStamp ?aOldTimeStamp}}" , - "forcedBindings" : { - "action" : { - "type" : "uri" , - "value" : ""} - } - } - , - "UPDATE_PROPERTY_VALUE" : { - "sparql" : " DELETE { ?property dul:hasDataValue ?oldValue} INSERT { ?property dul:hasDataValue ?value} WHERE { ?property rdf:type td:Property. ?property td:isWritable 'true'. ?property dul:hasDataValue ?oldValue }" , - "forcedBindings" : { - "value" : { - "type" : "literal" , - "value" : ""} - , - "property" : { - "type" : "uri" , - "value" : ""} - } - } - , - "UPDATE_ACTION_OUTPUT_VALUE" : { - "sparql" : " DELETE { ?instance td:hasOutput ?oldOutput . ?oldOutput rdf:type wot:ActionOutput . ?oldOutput dul:hasDataValue ?value } INSERT { ?instance td:hasOutput ?output. ?output rdf:type wot:ActionOutput . ?output dul:hasDataValue ?value } WHERE { ?action wot:hasInstance ?instance . ?instance rdf:type wot:ActionInstance . BIND(IRI(concat('http://wot.arces.unibo.it/sepa#ActionOutput_',STRUUID())) AS ?output) . OPTIONAL {?instance td:hasOutput ?oldOutput . ?oldOutput rdf:type wot:ActionOutput . ?oldOutput dul:hasDataValue ?value }}" , - "forcedBindings" : { - "value" : { - "type" : "literal" , - "value" : ""} - , - "action" : { - "type" : "uri" , - "value" : ""} - } - } - , - "UPDATE_DISCOVER" : { - "sparql" : " DELETE { ?thing wot:isDiscoverable ?oldValue } INSERT { ?thing wot:isDiscoverable ?value } WHERE {?thing wot:isDiscoverable ?oldValue}" , - "forcedBindings" : { - "value" : { - "type" : "literal" , - "value" : ""} - , - "thing" : { - "type" : "uri" , - "value" : ""} - } - } - } - , - "queries" : { - "ALL_THINGS" : { - "sparql" : "SELECT ?thing ?name ?discoverable WHERE { ?thing rdf:type td:Thing . ?thing td:hasName ?name . ?thing wot:isDiscoverable ?discoverable }"} - , - "THING_DESCRIPTION" : { - "sparql" : " SELECT ?name ?discoverable ?component ?property ?pName ?pStability ?pWrite ?pDataType ?pDataValue ?event ?eName ?eProperty ?action ?aName ?aProtocol ?aProperty WHERE { ?thing rdf:type td:Thing. ?thing wot:isDiscoverable ?discoverable . ?thing td:hasName ?name . OPTIONAL {?thing wot:hasComponent ?component. ?component rdf:type td:Thing} . OPTIONAL {?thing td:hasProperty ?property. ?property td:hasName ?pName. ?property td:hasStability ?pStability. ?property td:isWritable ?pWrite. ?pValueType rdf:type ?pDataType . ?pValueType dul:hasDataValue ?pDataValue} . OPTIONAL {?thing td:hasEvent ?event. ?event td:hasName ?eName. OPTIONAL {?event td:forProperty ?eProperty}} . OPTIONAL {?thing td:hasAction ?action. ?action td:hasName ?aName. ?action wot:isAccessibleBy ?aProtocol. OPTIONAL {?action td:forProperty ?aProperty}}}" , - "forcedBindings" : { - "thing" : { - "type" : "uri" , - "value" : ""} - } - } - , - "ACTION" : { - "sparql" : " SELECT ?value ?timeStamp WHERE {?action rdf:type td:Action. ?action wot:hasInstance ?instance. ?instance wot:hasTimeStamp ?timeStamp . OPTIONAL {?instance td:hasInput ?input. ?input dul:hasDataValue ?value}}" , - "forcedBindings" : { - "action" : { - "type" : "uri" , - "value" : ""} - } - } - , - "ACTION_OUTPUT_VALUE" : { - "sparql" : " SELECT ?value WHERE {?action rdf:type td:Action. ?action wot:hasInstance ?instance . ?instance td:hasOutput ?aOutput. ?aOutput dul:hasDataValue ?value}" , - "forcedBindings" : { - "action" : { - "type" : "uri" , - "value" : ""} - } - } - , - "EVENT" : { - "sparql" : "SELECT ?thing ?timeStamp ?value WHERE { ?event rdf:type td:Event. ?event wot:hasInstance ?instance. ?instance wot:isGeneratedBy ?thing. ?instance wot:hasTimeStamp ?timeStamp. OPTIONAL {?instance td:hasOutput ?output. ?output dul:hasDataValue ?value}}" , - "forcedBindings" : { - "event" : { - "type" : "uri" , - "value" : ""} - } - } - , - "THING_EVENT" : { - "sparql" : "SELECT ?timeStamp ?value WHERE {?event rdf:type td:Event. ?event wot:hasInstance ?instance. ?instance wot:isGeneratedBy ?thing. ?instance wot:hasTimeStamp ?timeStamp. OPTIONAL {?instance td:hasOutput ?output. ?output dul:hasDataValue ?value}}" , - "forcedBindings" : { - "event" : { - "type" : "uri" , - "value" : ""} - , - "thing" : { - "type" : "uri" , - "value" : ""} - } - } - , - "ALL_EVENTS" : { - "sparql" : " SELECT ?thing ?event ?eName WHERE { ?thing rdf:type td:Thing. ?thing td:hasEvent ?event. ?event td:hasName ?eName}" , - "forcedBindings" : { - } - } - , - "ALL_ACTIONS" : { - "sparql" : "SELECT ?thing ?action ?aName WHERE { ?thing rdf:type td:Thing . ?thing td:hasAction ?action. ?action td:hasName ?aName}" , - "forcedBindings" : { - } - } - , - "ALL_PROPERTIES" : { - "sparql" : "SELECT ?thing ?property ?pName WHERE { ?thing rdf:type td:Thing. ?thing td:hasProperty ?property. ?property td:hasName ?pName}" , - "forcedBindings" : { - } - } - } -} diff --git a/WebThings/src/main/resources/background.jpg b/WebThings/src/main/resources/background.jpg deleted file mode 100644 index a5b5f7da84eb87d0bcf5880ee4bb3b0723dfff5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 133059 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
    AXT7e93E=xVY{ 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�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 z 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 diff --git a/WebThings/src/test/java/org/WebThings/AppTest.java b/WebThings/src/test/java/org/WebThings/AppTest.java deleted file mode 100644 index d54508f2..00000000 --- a/WebThings/src/test/java/org/WebThings/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.WebThings; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java index 7c0a3397..52f19681 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java @@ -18,6 +18,7 @@ package it.unibo.arces.wot.sepa.api; +import it.unibo.arces.wot.sepa.api.protocol.websocket.SEPAWebsocketClient; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; @@ -39,7 +40,7 @@ public interface ISubscriptionHandler { * Notice that it is also called even after {@link SEPAWebsocketClient#close()} * is used. */ - void onBrokenSocket(); + void onBrokenConnection(); /** * This method is called if an error occurred diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java new file mode 100644 index 00000000..e2a1326d --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java @@ -0,0 +1,20 @@ +package it.unibo.arces.wot.sepa.api; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.Response; + +public interface ISubscriptionProtocol { + + void setHandler(ISubscriptionHandler handler) throws SEPAProtocolException; + + void close(); + + Response subscribe(String sparql); + + Response secureSubscribe(String sparql, String authorization); + + Response unsubscribe(String subscribeUUID); + + Response secureUnsubscribe(String subscribeUUID, String authorization); + +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java index cf0b27a0..c7460024 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java @@ -68,7 +68,7 @@ public class SPARQL11SEProperties extends SPARQL11Properties { /** The Constant logger. */ - private static final Logger logger = LogManager.getLogger("SPARQL11SEProperties"); + private static final Logger logger = LogManager.getLogger(); /** * The new primitives introduced by the SPARQL 1.1 SE Protocol are: @@ -240,6 +240,15 @@ protected void validate() throws SEPAPropertiesException { } } + public String getDefaultHost() { + try { + return jsap.get("sparql11seprotocol").getAsJsonObject().get("host").getAsString(); + } + catch(Exception e) { + return super.getDefaultHost(); + } + } + public String getSubscribePath() { try { return jsap.get("sparql11seprotocol").getAsJsonObject().get("availableProtocols").getAsJsonObject() diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java index 53808378..f8210752 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java @@ -55,10 +55,8 @@ import com.google.gson.JsonParser; import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties.SPARQL11SEPrimitive; -import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties.SubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.protocol.SSLSecurityManager; @@ -74,7 +72,6 @@ import it.unibo.arces.wot.sepa.commons.response.RegistrationResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; /** * This class implements the SPARQL 1.1 Secure event protocol with SPARQL 1.1 @@ -86,128 +83,34 @@ * 1.1 Subscribe Language */ public class SPARQL11SEProtocol extends SPARQL11Protocol { - private static final Logger logger = LogManager.getLogger("SPARQL11SEProtocol"); + private static final Logger logger = LogManager.getLogger(); - private SPARQL11SEWebsocket wsClient; - private SPARQL11SESecureWebsocket wssClient; - - protected SPARQL11SEProperties properties = null; - - public SPARQL11SEProtocol(SPARQL11SEProperties properties) throws SEPAProtocolException { - super(properties); - this.properties = properties; - } - - - public SPARQL11SEProtocol(ApplicationProfile appProfile, String id, boolean update) throws SEPAProtocolException, SEPASecurityException { - super(appProfile,id,update); - } - - public SPARQL11SEProtocol(ApplicationProfile appProfile, ISubscriptionHandler handler,String id) throws SEPAProtocolException, SEPASecurityException { - super(appProfile,id,false); - - if (handler == null) { - logger.fatal("Handler is null"); - throw new SEPAProtocolException(new IllegalArgumentException("Handler is null")); - } - - this.properties = appProfile; - - if (appProfile.getSubscribeProtocol(id).equals(SubscriptionProtocol.WS)) { - // WS - int port = appProfile.getSubscribePort(id); - if (port != -1) - wsClient = new SPARQL11SEWebsocket( - "ws://" + appProfile.getSubscribeHost(id) + ":" + port + appProfile.getSubscribePath(id), handler); - else - wsClient = new SPARQL11SEWebsocket("ws://" + appProfile.getSubscribeHost(id) + appProfile.getSubscribePath(id), - handler); - } - else if (appProfile.getSubscribeProtocol(id).equals(SubscriptionProtocol.WSS)) { - // WSS - int port = appProfile.getSubscribePort(id); - if (port != -1) - wssClient = new SPARQL11SESecureWebsocket("wss://" + appProfile.getSubscribeHost(id) + ":" + port - + appProfile.getSubscribePath(id), handler); - else - wssClient = new SPARQL11SESecureWebsocket( - "wss://" + appProfile.getSubscribeHost(id) + appProfile.getSubscribePath(id), - handler); - } - } + private SPARQL11SEProperties properties = null; + private ISubscriptionProtocol subscriptionProtocol; - /** - * Create a protocol instance to communicate with SEPA. In particular use this - * method if you want to subscribe about changes in the semantic graph. - * Otherwise use {@link #SPARQL11SEProtocol(SPARQL11SEProperties)} - * - * @param properties - * @param handler - * an handler to get notification about subscribe queries - * @throws SEPAProtocolException - * @throws SEPASecurityException - */ - public SPARQL11SEProtocol(SPARQL11SEProperties properties, ISubscriptionHandler handler) - throws SEPAProtocolException, SEPASecurityException { - super(properties); - - if (handler == null) { - logger.fatal("Handler is null"); - throw new SEPAProtocolException(new IllegalArgumentException("Handler is null")); + public SPARQL11SEProtocol(SPARQL11SEProperties properties,ISubscriptionProtocol protocol,ISubscriptionHandler handler) throws IllegalArgumentException, SEPAProtocolException { + if (protocol == null || handler == null || properties == null) { + logger.error("One or more arguments are null"); + throw new IllegalArgumentException("One or more arguments are null"); } - - this.properties = properties; - - if (properties.getSubscriptionProtocol().equals(SubscriptionProtocol.WS)) { - // WS - int port = properties.getSubscribePort(); - if (port != -1) - wsClient = new SPARQL11SEWebsocket( - "ws://" + properties.getHost() + ":" + port + properties.getSubscribePath(), handler); - else - wsClient = new SPARQL11SEWebsocket("ws://" + properties.getHost() + properties.getSubscribePath(), - handler); - } - else if (properties.getSubscriptionProtocol().equals(SubscriptionProtocol.WSS)) { - // WSS - int port = properties.getSubscribePort(); - if (port != -1) - wssClient = new SPARQL11SESecureWebsocket("wss://" + properties.getHost() + ":" + port - + properties.getSubscribePath(), handler); - else - wssClient = new SPARQL11SESecureWebsocket( - "wss://" + properties.getHost() + properties.getSubscribePath(), - handler); - } - } - - public String toString() { - return properties.toString(); + this.subscriptionProtocol = protocol; + this.subscriptionProtocol.setHandler(handler); + this.properties = properties; } /** * {@inheritDoc} */ - public Response update(UpdateRequest request,int timeout,HTTPMethod method) { - return super.update(request, timeout,method); - } - - public Response update(UpdateRequest request,int timeout) { - return super.update(request, timeout,HTTPMethod.POST); - } - public Response update(UpdateRequest request) { - //TODO: read default timeout from jsap - return super.update(request, 5000,HTTPMethod.POST); + return super.update(request); } - + /** * {@inheritDoc} */ public Response query(QueryRequest request) { - logger.debug(request.toString()); - return super.query(request, 0); + return super.query(request); } /** @@ -283,13 +186,11 @@ public long getTokenExpiringSeconds() throws SEPASecurityException { * * @throws IOException */ - @Override - public void close() throws IOException { - super.close(); - if (wsClient != null) { - wsClient.close(); - } - } + @Override + public void close() throws IOException { + super.close(); + subscriptionProtocol.close(); + } protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op) { return executeSPARQL11SEPrimitive(op, null); @@ -312,9 +213,7 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req case SECURESUBSCRIBE: SubscribeRequest subscribe = (SubscribeRequest) request; if (op == SPARQL11SEPrimitive.SUBSCRIBE) { - if (wsClient == null) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Secure mode: unsecure request not allowed"); - - return wsClient.subscribe(subscribe.getSPARQL()); + return subscriptionProtocol.subscribe(subscribe.getSPARQL()); } try { @@ -322,26 +221,19 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req } catch (SEPASecurityException e) { return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } - - if (wssClient == null) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Unsecure mode: secure request not allowed"); - - return wssClient.secureSubscribe(subscribe.getSPARQL(), "Bearer " + authorization); + + return subscriptionProtocol.secureSubscribe(subscribe.getSPARQL(), "Bearer " + authorization); case UNSUBSCRIBE: case SECUREUNSUBSCRIBE: UnsubscribeRequest unsubscribe = (UnsubscribeRequest) request; if (op == SPARQL11SEPrimitive.UNSUBSCRIBE) { - if (wsClient == null) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Secure mode: unsecure request not allowed"); - - return wsClient.unsubscribe(unsubscribe.getSubscribeUUID()); + return subscriptionProtocol.unsubscribe(unsubscribe.getSubscribeUUID()); } - - if (wssClient == null) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Unsecure mode: secure request not allowed"); - + try { - return wssClient.secureUnsubscribe(unsubscribe.getSubscribeUUID(), - "Bearer " + properties.getAccessToken()); - } catch (SEPASecurityException e) { - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return subscriptionProtocol.secureUnsubscribe(unsubscribe.getSubscribeUUID(),"Bearer " + properties.getAccessToken()); + } catch (SEPASecurityException e3) { + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, e3.getMessage()); } default: break; @@ -371,7 +263,7 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req } catch (URISyntaxException e1) { return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); } - + String basic; try { basic = properties.getBasicAuthorization(); @@ -381,7 +273,6 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req if (basic == null) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Basic authorization in null. Register first"); - authorization = "Basic " + basic; contentType = "application/json"; accept = "application/json"; @@ -408,7 +299,7 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req body.setContentType(contentType); break; case SECUREQUERY: - path = properties.getQueryPath(); + path = properties.getDefaultQueryPath(); port = properties.getHttpsPort(); accept = "application/sparql-results+json"; @@ -431,7 +322,8 @@ protected Response executeSPARQL11SEPrimitive(SPARQL11SEPrimitive op, Object req // POST request try { - if (uri == null) uri = new URI("https", null, properties.getHost(), port, path, null, null); + if (uri == null) + uri = new URI("https", null, properties.getDefaultHost(), port, path, null, null); } catch (URISyntaxException e1) { return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SEPAWebsocketClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java similarity index 95% rename from client-api/src/main/java/it/unibo/arces/wot/sepa/api/SEPAWebsocketClient.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java index d85fb09e..430705d2 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SEPAWebsocketClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java @@ -1,4 +1,4 @@ -package it.unibo.arces.wot.sepa.api; +package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.net.URI; @@ -10,6 +10,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; @@ -65,7 +66,7 @@ public void onOpen(ServerHandshake handshakedata) { @Override public void onClose(int code, String reason, boolean remote) { logger.debug("@onClose code: " + code + " reason: " + reason + " remote: " + remote); - if (handler != null) handler.onBrokenSocket(); + if (handler != null) handler.onBrokenConnection(); } @Override diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SESecureWebsocket.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java similarity index 95% rename from client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SESecureWebsocket.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java index 3d3ec08b..cd81b364 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SESecureWebsocket.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java @@ -1,4 +1,4 @@ -package it.unibo.arces.wot.sepa.api; +package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.io.File; import java.io.FileInputStream; @@ -77,9 +77,9 @@ protected boolean connect() { return true; } - public SPARQL11SESecureWebsocket(String wsUrl, ISubscriptionHandler handler) + public SPARQL11SESecureWebsocket(String wsUrl) throws SEPAProtocolException, SEPASecurityException { - super(wsUrl, handler); + super(wsUrl); KeyStore ks; try { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEWebsocket.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java similarity index 75% rename from client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEWebsocket.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java index b36fec60..1c5dff08 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEWebsocket.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java @@ -1,4 +1,4 @@ -package it.unibo.arces.wot.sepa.api; +package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.net.URI; import java.net.URISyntaxException; @@ -9,12 +9,13 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; public class SPARQL11SEWebsocket { - protected Logger logger = LogManager.getLogger("SPARQL11SEWebsocket"); + protected Logger logger = LogManager.getLogger(); private long TIMEOUT = 5000; @@ -22,16 +23,19 @@ public class SPARQL11SEWebsocket { protected URI wsURI = null; protected ISubscriptionHandler handler = null; - public SPARQL11SEWebsocket(String wsUrl, ISubscriptionHandler handler) throws SEPAProtocolException { - try { - if (handler == null) { - logger.fatal("Notification handler is null. Client cannot be initialized"); - throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); - } + public SPARQL11SEWebsocket(String wsUrl) throws SEPAProtocolException { + try { wsURI = new URI(wsUrl); } catch (URISyntaxException e) { throw new SEPAProtocolException(e); } + } + + public void setHandler(ISubscriptionHandler handler) throws SEPAProtocolException { + if (handler == null) { + logger.fatal("Notification handler is null. Client cannot be initialized"); + throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); + } this.handler = handler; } @@ -44,7 +48,12 @@ public Response subscribe(String sparql) { return _subscribe(sparql, null, null); } - protected boolean connect() { + protected boolean connect() throws SEPAProtocolException { + if (handler == null) { + logger.fatal("Notification handler is null. Client cannot be initialized"); + throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); + } + if (client == null) try { client = new SEPAWebsocketClient(wsURI, handler); @@ -63,8 +72,12 @@ protected Response _subscribe(String sparql, String authorization, String alias) if (sparql == null) return new ErrorResponse(500, "SPARQL query is null"); - if (!connect()) { - return new ErrorResponse(500, "Failed to connect"); + try { + if (!connect()) { + return new ErrorResponse(500, "Failed to connect"); + } + } catch (SEPAProtocolException e1) { + return new ErrorResponse(500,e1.getMessage()); } if (!client.isOpen()) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java new file mode 100644 index 00000000..086ba4ba --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java @@ -0,0 +1,81 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import org.apache.http.HttpStatus; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; + +public class WebSocketSubscriptionProtocol implements ISubscriptionProtocol { + private SPARQL11SEWebsocket wsClient = null; + private SPARQL11SESecureWebsocket wssClient = null; + + public WebSocketSubscriptionProtocol(String host, int port, String path, boolean secure) + throws SEPAProtocolException, SEPASecurityException { + if (!secure) { + // WS + if (port != -1) + wsClient = new SPARQL11SEWebsocket("ws://" + host + ":" + port + path); + else + wsClient = new SPARQL11SEWebsocket("ws://" + host + path); + } else { + // WSS + if (port != -1) + wssClient = new SPARQL11SESecureWebsocket("wss://" + host + ":" + port + path); + else + wssClient = new SPARQL11SESecureWebsocket("wss://" + host + path); + } + } + + @Override + public void setHandler(ISubscriptionHandler handler) throws SEPAProtocolException { + if (wsClient != null) + wsClient.setHandler(handler); + else if (wssClient != null) + wssClient.setHandler(handler); + } + + @Override + public void close() { + if (wsClient != null) + wsClient.close(); + if (wssClient != null) + wssClient.close(); + } + + @Override + public Response subscribe(String sparql) { + if (wsClient == null) + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); + + return wsClient.subscribe(sparql); + } + + @Override + public Response secureSubscribe(String sparql, String authorization) { + if (wssClient == null) + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); + + return wssClient.secureSubscribe(sparql, authorization); + } + + @Override + public Response unsubscribe(String subscribeUUID) { + if (wsClient == null) + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); + + return wsClient.unsubscribe(subscribeUUID); + } + + @Override + public Response secureUnsubscribe(String subscribeUUID, String authorization) { + if (wssClient == null) + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); + + return wssClient.secureUnsubscribe(subscribeUUID, authorization); + } + +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java index 750b5ef0..a43f143c 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java @@ -262,10 +262,18 @@ protected void storeProperties(String propertiesFile) throws SEPAPropertiesExcep * * @return the host (default is localhost) */ - public String getHost() { - if (jsap.get("sparql11protocol").getAsJsonObject().get("host") != null) + public String getDefaultHost() { + try { return jsap.get("sparql11protocol").getAsJsonObject().get("host").getAsString(); - return jsap.get("host").getAsString(); + } + catch(Exception e) { + try { + return jsap.get("host").getAsString(); + } + catch(Exception e1) { + return null; + } + } } /** @@ -273,7 +281,7 @@ public String getHost() { * * @return the update port */ - public int getHttpPort() { + public int getDefaultPort() { try { return jsap.get("sparql11protocol").getAsJsonObject().get("port").getAsInt(); } catch (Exception e) { @@ -399,7 +407,7 @@ public String getUpdateAcceptHeader() { * * @return the query path (default is /query) */ - public String getQueryPath() { + public String getDefaultQueryPath() { return jsap.get("sparql11protocol").getAsJsonObject().get("query").getAsJsonObject().get("path").getAsString(); } @@ -468,7 +476,7 @@ public String getQueryContentTypeHeader() { } } - public String getProtocolScheme() { + public String getDefaultProtocolScheme() { return jsap.get("sparql11protocol").getAsJsonObject().get("protocol").getAsString(); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index b5439d57..5b93fc1e 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -19,36 +19,41 @@ package it.unibo.arces.wot.sepa.commons.protocol; import java.io.IOException; +import java.io.InterruptedIOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; +import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.net.ssl.SSLException; + import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.ConnectTimeoutException; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.request.Request; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; import it.unibo.arces.wot.sepa.timing.Timings; import org.apache.logging.log4j.Logger; @@ -71,78 +76,20 @@ public class SPARQL11Protocol implements java.io.Closeable { /** The http client. */ final CloseableHttpClient httpClient = HttpClients.createDefault(); - - /** The url components. */ - private String scheme = "http"; - private String host = "localhost"; - private int port = -1; - private String updatePath = "/update"; - private String queryPath = "/query"; - - /** Endpoint authentication */ - private boolean authentication = false; - private String authorizationHeader = null; - public SPARQL11Protocol(SPARQL11Properties properties) throws SEPAProtocolException { - if (properties == null) { - logger.fatal("Properties are null"); - throw new SEPAProtocolException(new IllegalArgumentException("Properties are null")); - } - - this.scheme = properties.getProtocolScheme(); - this.host = properties.getHost(); - this.port = properties.getHttpPort(); - this.updatePath = properties.getUpdatePath(); - this.queryPath = properties.getQueryPath(); - - this.authentication = properties.isAuthenticationRequired(); - this.authorizationHeader = properties.getAuthorizationHeader(); - } - - public SPARQL11Protocol(ApplicationProfile appProfile, String id, boolean update) throws SEPAProtocolException { - if (scheme == null | host == null | updatePath == null | queryPath == null) { - logger.fatal("Properties are null"); - throw new SEPAProtocolException(new IllegalArgumentException("Properties are null")); - } - if (update) { - this.scheme = appProfile.getUpdateProtocol(id); - this.host = appProfile.getUpdateHost(id); - this.port = appProfile.getUpdatePort(id); - this.updatePath = appProfile.getUpdatePath(id); - this.queryPath = appProfile.getQueryPath(); - - this.authentication = appProfile.isAuthenticationRequiredForUpdate(id); - this.authorizationHeader = appProfile.getUpdateAuthorizationHeader(id); - } else { - this.scheme = appProfile.getQueryProtocol(id); - this.host = appProfile.getQueryHost(id); - this.port = appProfile.getQueryPort(id); - this.updatePath = appProfile.getUpdatePath(); - this.queryPath = appProfile.getQueryPath(id); - - this.authentication = appProfile.isAuthenticationRequiredForQuery(id); - this.authorizationHeader = appProfile.getQueryAuthorizationHeader(id); - } - } - - private Response executeRequest(HttpUriRequest req, int timeout, boolean update, int token) { + private Response executeRequest(HttpUriRequest req, Request request) { CloseableHttpResponse httpResponse = null; HttpEntity responseEntity = null; int responseCode = 0; String responseBody = null; try { - // Add "Authorization" header if required - if (authentication) { - req.setHeader("Authorization", authorizationHeader); - } - // Execute HTTP request - logger.debug("Execute HTTP request (timeout: " + timeout + " ms) " + req.toString(), timeout); + logger.debug(req.toString()+" (timeout: " + request.getTimeout() + " ms) "); long start = Timings.getTime(); httpResponse = httpClient.execute(req); long stop = Timings.getTime(); - if (update) + if (request.getClass().equals(UpdateRequest.class)) Timings.log("ENDPOINT_UPDATE_TIME", start, stop); else Timings.log("ENDPOINT_QUERY_TIME", start, stop); @@ -153,35 +100,46 @@ private Response executeRequest(HttpUriRequest req, int timeout, boolean update, // Body responseEntity = httpResponse.getEntity(); responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); - logger.debug(String.format("Response (%d): %s", responseCode, responseBody)); + logger.debug(String.format("Response code: %d", responseCode)); EntityUtils.consume(responseEntity); + + // http://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/fundamentals.html#d5e279 } catch (IOException e) { - return new ErrorResponse(token, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } finally { + if (e instanceof InterruptedIOException) { + return new ErrorResponse(request.getToken(), HttpStatus.SC_SERVICE_UNAVAILABLE, e.getMessage()); + } + if (e instanceof UnknownHostException) { + return new ErrorResponse(request.getToken(), HttpStatus.SC_NOT_FOUND, e.getMessage()); + } + if (e instanceof ConnectTimeoutException) { + return new ErrorResponse(request.getToken(), HttpStatus.SC_REQUEST_TIMEOUT, e.getMessage()); + } + if (e instanceof SSLException) { + return new ErrorResponse(request.getToken(), HttpStatus.SC_UNAUTHORIZED, e.getMessage()); + } + return new ErrorResponse(request.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + finally { try { if (httpResponse != null) httpResponse.close(); } catch (IOException e) { - return new ErrorResponse(token, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(request.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } responseEntity = null; } - if (responseCode >= 400) { - return new ErrorResponse(token, responseCode, responseBody); -// try { -//// return new ErrorResponse(token, new JsonParser().parse(responseBody).getAsJsonObject()); -//// } catch (Exception e) { -//// return new ErrorResponse(token, responseCode, responseBody); -//// } - } - if (update) - return new UpdateResponse(token, responseBody); + if (responseCode >= 400) + return new ErrorResponse(request.getToken(), responseCode, responseBody); + + if (request.getClass().equals(UpdateRequest.class)) + return new UpdateResponse(request.getToken(), responseBody); + try { - return new QueryResponse(token, new JsonParser().parse(responseBody).getAsJsonObject()); + return new QueryResponse(request.getToken(), new JsonParser().parse(responseBody).getAsJsonObject()); } catch (Exception e) { - return new ErrorResponse(token, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(request.getToken(), HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, e.getMessage()); } } @@ -235,27 +193,23 @@ private Response executeRequest(HttpUriRequest req, int timeout, boolean update, * success or failure of the request via HTTP response status code. * */ - // public Response update(UpdateRequest req, int timeout) { - // return post(req, timeout, false); - // } - - public Response update(UpdateRequest req, int timeout, HTTPMethod method) { - switch (method) { + public Response update(UpdateRequest req) { + switch (req.getHttpMethod()) { case GET: // *********************** // OpenLink VIRTUOSO PATCH (not supported by SPARQL 1.1 Protocol) // *********************** - return patchVirtuoso(req, timeout); + return patchVirtuoso(req); case POST: - return post(req, timeout, false); + return post(req); case URL_ENCODED_POST: - return post(req, timeout, true); + return post(req); } - return post(req, timeout, false); + return post(req); } - private Response post(UpdateRequest req, int timeout, boolean urlEncoded) { + private Response post(UpdateRequest req) { StringEntity requestEntity = null; HttpPost post; String graphs = ""; @@ -264,45 +218,59 @@ private Response post(UpdateRequest req, int timeout, boolean urlEncoded) { if (req.getUsingGraphUri() != null) { graphs += "using-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); - + //graphs += "using-graph-uri=" + req.getUsingGraphUri(); + if (req.getUsingNamedGraphUri() != null) { + //graphs += "&using-named-graph-uri=" + req.getUsingNamedGraphUri(); graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); } } else if (req.getUsingNamedGraphUri() != null) { graphs += "using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); +// graphs += "using-named-graph-uri=" + req.getUsingNamedGraphUri(); } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } + // Setting URL + String scheme = req.getScheme(); + String host = req.getHost(); + int port = req.getPort(); + String updatePath = req.getPath(); + String authorizationHeader = req.getAuthorizationHeader(); + try { - if (!urlEncoded) + if (req.getHttpMethod().equals(HTTPMethod.POST)) { post = new HttpPost(new URI(scheme, null, host, port, updatePath, graphs, null)); - else + post.setHeader("Content-Type", "application/sparql-update"); + } else { post = new HttpPost(new URI(scheme, null, host, port, updatePath, null, null)); + post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + } } catch (URISyntaxException e) { logger.error(e.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } post.setHeader("Accept", req.getAcceptHeader()); - if (!urlEncoded) - post.setHeader("Content-Type", "application/sparql-update"); - else - post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + // Add "Authorization" header if required + if (authorizationHeader != null) { + post.setHeader("Authorization", authorizationHeader); + } requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); post.setEntity(requestEntity); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) - .build(); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) + .setConnectTimeout(req.getTimeout()).build(); post.setConfig(requestConfig); - return executeRequest(post, timeout, true, req.getToken()); + return executeRequest(post, req); } - private Response patchVirtuoso(UpdateRequest req, int timeout) { + private Response patchVirtuoso(UpdateRequest req) { // 1) "INSERT DATA" is not supported. Only INSERT (also if the WHERE is not // present). String fixedSparql = req.getSPARQL(); @@ -326,13 +294,12 @@ private Response patchVirtuoso(UpdateRequest req, int timeout) { } catch (Exception e) { return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } - + // 2) SPARQL 1.1 Update are issued as GET request (like for a SPARQL 1.1 Query) String query; try { // custom "format" parameter - query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format=" - + URLEncoder.encode(req.getAcceptHeader(), "UTF-8"); + query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format="+ URLEncoder.encode(req.getAcceptHeader(), "UTF-8"); } catch (UnsupportedEncodingException e1) { logger.error(e1.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); @@ -343,13 +310,13 @@ private Response patchVirtuoso(UpdateRequest req, int timeout) { try { if (req.getUsingGraphUri() != null) { - graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); + graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(),"UTF-8"); if (req.getUsingNamedGraphUri() != null) { - graphs += "&named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + graphs += "&named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(),"UTF-8"); } } else if (req.getUsingNamedGraphUri() != null) { - graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(),"UTF-8"); } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); @@ -359,22 +326,36 @@ private Response patchVirtuoso(UpdateRequest req, int timeout) { if (!graphs.equals("")) query += "&" + graphs; + logger.debug("Query: "+query); + + // Setting URL + String scheme = req.getScheme(); + String host = req.getHost(); + int port = req.getPort(); + String queryPath = req.getPath(); + String authorizationHeader = req.getAuthorizationHeader(); + String url; if (port != -1) - url = "http://" + host + ":" + port + queryPath + "?" + query; + url = scheme + "://" + host + ":" + port + queryPath + "?" + query; else - url = "http://" + host + queryPath + "?" + query; + url = scheme + "://" + host + queryPath + "?" + query; HttpGet get; get = new HttpGet(url); get.setHeader("Accept", req.getAcceptHeader()); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) - .build(); + // Add "Authorization" header if required + if (authorizationHeader != null) { + get.setHeader("Authorization", authorizationHeader); + } + + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) + .setConnectTimeout(req.getTimeout()).build(); get.setConfig(requestConfig); - return executeRequest(get, timeout, false, req.getToken()); + return executeRequest(get, req); } /** @@ -457,70 +438,78 @@ private Response patchVirtuoso(UpdateRequest req, int timeout) { * * */ - public Response query(QueryRequest req, int timeout) { - return post(req, timeout, false); - } - - public Response query(QueryRequest req, int timeout, HTTPMethod method) { - switch (method) { + public Response query(QueryRequest req) { + switch (req.getHttpMethod()) { case GET: - return get(req, timeout); + return get(req); case POST: - return post(req, timeout, false); + return post(req); case URL_ENCODED_POST: - return post(req, timeout, true); + return post(req); } - return post(req, timeout, false); + return post(req); } - private Response post(QueryRequest req, int timeout, boolean urlEncoded) { + private Response post(QueryRequest req) { StringEntity requestEntity = null; HttpPost post; + + // Graphs String graphs = ""; - try { if (req.getDefaultGraphUri() != null) { - graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); + graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(),"UTF-8"); if (req.getNamedGraphUri() != null) { - graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + graphs += "&named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(),"UTF-8"); } } else if (req.getNamedGraphUri() != null) { - graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(),"UTF-8"); } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } + // Setting URL + String scheme = req.getScheme(); + String host = req.getHost(); + int port = req.getPort(); + String queryPath = req.getPath(); + String authorizationHeader = req.getAuthorizationHeader(); + try { - if (!urlEncoded) + if (req.getHttpMethod().equals(HTTPMethod.POST)) { post = new HttpPost(new URI(scheme, null, host, port, queryPath, graphs, null)); - else + post.setHeader("Content-Type", "application/sparql-query"); + } else { post = new HttpPost(new URI(scheme, null, host, port, queryPath, null, null)); + post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + } } catch (URISyntaxException e) { logger.error(e.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } post.setHeader("Accept", req.getAcceptHeader()); - if (!urlEncoded) - post.setHeader("Content-Type", "application/sparql-query"); - else - post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + // Add "Authorization" header if required + if (authorizationHeader != null) { + post.setHeader("Authorization", authorizationHeader); + } requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); post.setEntity(requestEntity); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) - .build(); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) + .setConnectTimeout(req.getTimeout()).build(); post.setConfig(requestConfig); - return executeRequest(post, timeout, false, req.getToken()); + return executeRequest(post, req); } - private Response get(QueryRequest req, int timeout) { + private Response get(QueryRequest req) { String query; try { query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"); @@ -533,13 +522,13 @@ private Response get(QueryRequest req, int timeout) { try { if (req.getDefaultGraphUri() != null) { - graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); + graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(),"UTF-8"); if (req.getNamedGraphUri() != null) { - graphs += "&named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + graphs += "&named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(),"UTF-8"); } } else if (req.getNamedGraphUri() != null) { - graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(),"UTF-8"); } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); @@ -550,21 +539,33 @@ private Response get(QueryRequest req, int timeout) { query += "&" + graphs; String url; + // Setting URL + String scheme = req.getScheme(); + String host = req.getHost(); + int port = req.getPort(); + String queryPath = req.getPath(); + String authorizationHeader = req.getAuthorizationHeader(); + if (port != -1) - url = "http://" + host + ":" + port + queryPath + "?" + query; + url = scheme + "://" + host + ":" + port + queryPath + "?" + query; else - url = "http://" + host + queryPath + "?" + query; + url = scheme + "://" + host + queryPath + "?" + query; HttpGet get; get = new HttpGet(url); get.setHeader("Accept", req.getAcceptHeader()); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout) + // Add "Authorization" header if required + if (authorizationHeader != null) { + get.setHeader("Authorization", authorizationHeader); + } + + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()).setConnectTimeout(req.getTimeout()) .build(); get.setConfig(requestConfig); - - return executeRequest(get, timeout, false, req.getToken()); + + return executeRequest(get, req); } @Override diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index b33131d5..44c75649 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -18,6 +18,11 @@ package it.unibo.arces.wot.sepa.commons.request; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; + // TODO: Auto-generated Javadoc /** * This class represents a request to perform a SPARQL 1.1 Query @@ -48,6 +53,27 @@ public QueryRequest(String sparql) { super(sparql); } + public QueryRequest(String sparql,String default_graph_uri,String named_graph_uri) throws UnsupportedEncodingException { + super(sparql); + + this.default_graph_uri = default_graph_uri; + this.named_graph_uri = named_graph_uri; + } + + public QueryRequest(Integer token,HTTPMethod method,String scheme,String host, int port, String path,String sparql,int timeout,String default_graph_uri,String named_graph_uri) throws UnsupportedEncodingException { + super(token,sparql); + + super.method = method; + super.host = host; + super.port = port; + super.path = path; + super.timeout = timeout; + super.scheme = scheme; + + this.default_graph_uri = default_graph_uri; + this.named_graph_uri = named_graph_uri; + } + /* (non-Javadoc) * @see java.lang.Object#toString() */ @@ -61,20 +87,13 @@ public String getAcceptHeader() { return "application/sparql-results+json"; } - public String getDefaultGraphUri() { - return default_graph_uri; - } - - public void setDefaultGraphUri(String graphUri) { - this.default_graph_uri = graphUri; + public String getDefaultGraphUri() throws UnsupportedEncodingException { + if (default_graph_uri == null) return null; + return URLDecoder.decode(default_graph_uri,"UTF-8"); } - public String getNamedGraphUri() { - return named_graph_uri; + public String getNamedGraphUri() throws UnsupportedEncodingException { + if (named_graph_uri == null) return null; + return URLDecoder.decode(named_graph_uri,"UTF-8"); } - - public void setNamedGraphUri(String graphUri) { - this.named_graph_uri = graphUri; - } - } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java index 7556c6aa..7db65502 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java @@ -20,6 +20,8 @@ import java.io.UnsupportedEncodingException; import java.util.Base64; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; + // TODO: Auto-generated Javadoc /** * This class represents a generic request (i.e., QUERY, UPDATE, SUBSCRIBE, @@ -41,13 +43,23 @@ public abstract class Request { * https://tools.ietf.org/html/rfc7617 */ - private enum AUTHENTICATION_SCHEMA { + protected enum AUTHENTICATION_SCHEMA { DISABLED, BASIC }; - private AUTHENTICATION_SCHEMA authorization = AUTHENTICATION_SCHEMA.DISABLED; - private String basicAuthorizationHeader; + protected AUTHENTICATION_SCHEMA authorization = AUTHENTICATION_SCHEMA.DISABLED; + protected String basicAuthorizationHeader; + + protected HTTPMethod method = HTTPMethod.POST; + protected String id = null; + protected int timeout = 5000; + + protected String scheme = null; + protected String host = null; + protected int port = -1; + protected String path = null; + /** * Instantiates a new request. * @@ -127,12 +139,43 @@ public String getAuthorizationHeader() { case BASIC: return basicAuthorizationHeader; default: - return ""; + return null; } } public boolean isAuthenticationRequired() { return authorization != AUTHENTICATION_SCHEMA.DISABLED; } + + public HTTPMethod getHttpMethod() { + return method; + } + + public String getID() { + return id; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + public String getScheme() { + return scheme; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getPath() { + return path; + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java index 89d96ccf..c9c28e2e 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java @@ -35,7 +35,7 @@ public class SubscribeRequest extends QueryRequest { * @param sparql the sparql */ public SubscribeRequest(String sparql) { - super(sparql); + this(-1,sparql); } /** @@ -45,7 +45,7 @@ public SubscribeRequest(String sparql) { * @param alias the alias */ public SubscribeRequest(String sparql,String alias) { - super(sparql); + this(-1,sparql,alias); this.alias = alias; } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java index dfb7d090..42b456a3 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java @@ -18,6 +18,10 @@ package it.unibo.arces.wot.sepa.commons.request; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.request.Request; // TODO: Auto-generated Javadoc @@ -31,7 +35,7 @@ public class UpdateRequest extends Request { * that contains an operation that uses the USING, USING NAMED, or WITH clause. */ private String using_graph_uri = null; - private String named_graph_uri = null; + private String using_named_graph_uri = null; /** * Instantiates a new update request. @@ -51,6 +55,27 @@ public UpdateRequest(Integer token, String sparql) { public UpdateRequest(String sparql) { super(sparql); } + + public UpdateRequest(Integer token,HTTPMethod method,String scheme,String host, int port, String path,String sparql,int timeout,String using_graph_uri,String using_named_graph_uri) { + super(token,sparql); + + super.method = method; + super.host = host; + super.port = port; + super.path = path; + super.timeout = timeout; + super.scheme = scheme; + + this.using_graph_uri = using_graph_uri; + this.using_named_graph_uri = using_named_graph_uri; + } + + public UpdateRequest(String sparql,String using_graph_uri,String using_named_graph_uri) { + super(sparql); + + this.using_graph_uri = using_graph_uri; + this.using_named_graph_uri = using_named_graph_uri; + } /* (non-Javadoc) * @see java.lang.Object#toString() @@ -69,20 +94,14 @@ public String toString() { * using-graph-uri and using-named-graph-uri parameters. */ - public String getUsingGraphUri() { - return using_graph_uri; - } - - public void setUsingGraphUri(String using_graph_uri) { - this.using_graph_uri = using_graph_uri; - } - - public String getUsingNamedGraphUri() { - return named_graph_uri; + public String getUsingGraphUri() throws UnsupportedEncodingException { + if (using_graph_uri == null) return null; + return URLDecoder.decode(using_graph_uri,"UTF-8"); } - public void setNamedGraphUri(String named_graph_uri) { - this.named_graph_uri = named_graph_uri; + public String getUsingNamedGraphUri() throws UnsupportedEncodingException { + if (using_named_graph_uri == null) return null; + return URLDecoder.decode(using_named_graph_uri,"UTF-8"); } public String getAcceptHeader() { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java index f8b0a60a..ce9467d1 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/Bindings.java @@ -24,15 +24,15 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; // TODO: Auto-generated Javadoc /** * This class represents a query solution of a SPARQL 1.1 Query * - * An example of the internal representation as JSON object follows: - * {@code - * { "x" : { "type": "bnode", "value": "r2" }, "hpage" : { "type": "uri", - * "value": "http://work.example.org/alice/" }, "blurb" : { "datatype": + * An example of the internal representation as JSON object follows: {@code { + * "x" : { "type": "bnode", "value": "r2" }, "hpage" : { "type": "uri", "value": + * "http://work.example.org/alice/" }, "blurb" : { "datatype": * "http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral", "type": "literal", * "value": "

    My name is alice *

    @@ -75,6 +75,23 @@ public Set getVariables() { return ret; } + /** + * Gets the binding value. + * + * @param variable + * the variable + * @return the binding value + */ + public void setBindingValue(String variable,String value) throws IllegalArgumentException { + if (variable == null || value == null) throw new IllegalArgumentException("One or more arguments are null"); + try { + solution.get(variable).getAsJsonObject().add("value", new JsonPrimitive(value)); + } + catch(Exception e) { + throw new IllegalArgumentException(String.format("Variable not found: %s",variable)); + } + } + /** * Gets the binding value. * @@ -83,10 +100,12 @@ public Set getVariables() { * @return the binding value */ public String getBindingValue(String variable) { - if (solution.get(variable) == null) + try { + return solution.get(variable).getAsJsonObject().get("value").getAsString(); + } + catch(Exception e) { return null; - JsonObject json = solution.get(variable).getAsJsonObject(); - return json.get("value").getAsString(); + } } /** @@ -94,15 +113,16 @@ public String getBindingValue(String variable) { * * @param variable * the variable - * @return the datatype + * @return the datatype or null if the variable is not found, the term is not a + * literal or the datatype has not been specified */ public String getDatatype(String variable) { - if (solution.get(variable) == null) - return null; - JsonObject json = solution.get(variable).getAsJsonObject(); - if (json.get("datatype") == null) + try { + return solution.get(variable).getAsJsonObject().get("datatype").getAsString(); + } + catch(Exception e) { return null; - return json.get("datatype").getAsString(); + } } /** @@ -110,15 +130,16 @@ public String getDatatype(String variable) { * * @param variable * the variable - * @return the language + * @return the language or null if the variable is not found, the term is not a + * literal or the language has not been specified */ public String getLanguage(String variable) { - if (solution.get(variable) == null) - return null; - JsonObject json = solution.get(variable).getAsJsonObject(); - if (json.get("xml:lang") == null) + try { + return solution.get(variable).getAsJsonObject().get("xml:lang").getAsString(); + } + catch(Exception e) { return null; - return json.get("xml:lang").getAsString(); + } } /** @@ -128,11 +149,11 @@ public String getLanguage(String variable) { * the variable * @return true, if is literal */ - public boolean isLiteral(String variable) { - if (solution.get(variable) == null) - return false; - JsonObject json = solution.get(variable).getAsJsonObject(); - return json.get("type").getAsString().equals("literal"); + public boolean isLiteral(String variable) throws IllegalArgumentException { + if (!solution.has(variable)) + throw new IllegalArgumentException("Variable not found"); + + return (solution.get(variable).getAsJsonObject().get("type").getAsString().equals("literal") || solution.get(variable).getAsJsonObject().get("type").getAsString().equals("typed-literal")); } /** @@ -142,11 +163,11 @@ public boolean isLiteral(String variable) { * the variable * @return true, if is uri */ - public boolean isURI(String variable) { - if (solution.get(variable) == null) - return false; - JsonObject json = solution.get(variable).getAsJsonObject(); - return json.get("type").getAsString().equals("uri"); + public boolean isURI(String variable) throws IllegalArgumentException { + if (!solution.has(variable)) + throw new IllegalArgumentException("Variable not found"); + + return solution.get(variable).getAsJsonObject().get("type").getAsString().equals("uri"); } /** @@ -157,10 +178,10 @@ public boolean isURI(String variable) { * @return true, if is b node */ public boolean isBNode(String variable) { - if (solution.get(variable) == null) - return false; - JsonObject json = solution.get(variable).getAsJsonObject(); - return json.get("type").getAsString().equals("bnode"); + if (!solution.has(variable)) + throw new IllegalArgumentException("Variable not found"); + + return solution.get(variable).getAsJsonObject().get("type").getAsString().equals("bnode"); } /** diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java index e230c848..bf873082 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTerm.java @@ -33,9 +33,10 @@ public abstract class RDFTerm { /** * Gets the value. * - * @return the value + * @return the value or null if tha value has not been assigned */ public String getValue() { + if (!json.has("value")) return null; return json.get("value").getAsString(); } @@ -65,9 +66,10 @@ public JsonObject toJson() { * @param value * the value */ - public RDFTerm(String value) { + public RDFTerm(String value) throws IllegalArgumentException { json = new JsonObject(); - json.add("value", new JsonPrimitive(value)); + + if (value != null) json.add("value", new JsonPrimitive(value)); } /* diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java index b81f8ba2..58ebb787 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermLiteral.java @@ -22,12 +22,19 @@ import it.unibo.arces.wot.sepa.commons.sparql.RDFTerm; -// TODO: Auto-generated Javadoc /** * The Class RDFTermLiteral. */ public class RDFTermLiteral extends RDFTerm { + /** + * Instantiates a new RDF term literal with a null value + * + */ + public RDFTermLiteral() { + this(null,null,null); + } + /** * Instantiates a new RDF term literal. * @@ -35,47 +42,58 @@ public class RDFTermLiteral extends RDFTerm { * the value */ public RDFTermLiteral(String value) { - super(value); - - json.add("type", new JsonPrimitive("literal")); + this(value,null,null); } /** * Instantiates a new RDF term literal. - * - * @param value - * the value - * @param lanOrDT - * the lan or DT - * @param lan - * the lan + * + * @param value the value + * + * @param datatype a XSD datatype (https://www.w3.org/TR/xmlschema-2/) + * + * @param language if not null, it is a language identifier (RFC3066 (http://www.ietf.org/rfc/rfc3066.txt)) and the datatype is set to "xsd:string" by default */ - public RDFTermLiteral(String value, String lanOrDT, boolean lan) { + public RDFTermLiteral(String value, String datatype, String language) { super(value); json.add("type", new JsonPrimitive("literal")); - - if (lan) - json.add("xml:lang", new JsonPrimitive(lanOrDT)); - else - json.add("datatype", new JsonPrimitive(lanOrDT)); + + if (language != null) { + json.add("xml:lang", new JsonPrimitive(language)); + json.add("datatype", new JsonPrimitive("xsd:string")); + } + else if (datatype != null) json.add("datatype", new JsonPrimitive(datatype)); } + /** + * Instantiates a new RDF term literal. + * + * @param value the value + * + * @param datatype an XSD datatype (https://www.w3.org/TR/xmlschema-2/) + */ + public RDFTermLiteral(String value, String datatype) { + this(value,datatype,null); + } + /** * Gets the language tag. * - * @return the language tag + * @return the language tag or null if it is not defined */ public String getLanguageTag() { + if (!json.has("xml:lang")) return null; return json.get("xml:lang").getAsString(); } /** * Gets the datatype. * - * @return the datatype + * @return the datatype or null if it is not defined */ public String getDatatype() { + if (!json.has("datatype")) return null; return json.get("datatype").getAsString(); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java index 2e147398..b65b1d92 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/sparql/RDFTermURI.java @@ -38,4 +38,12 @@ public RDFTermURI(String value) { super(value); json.add("type", new JsonPrimitive("uri")); } + + /** + * Instantiates a new RDF term URI with null value + * + */ + public RDFTermURI() { + this(null); + } } \ No newline at end of file diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java index 2ad002da..6bfbab9b 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java @@ -25,12 +25,12 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; public abstract class Aggregator extends Consumer implements IConsumer,IProducer { protected String sparqlUpdate = null; protected String SPARQL_ID = ""; + protected Bindings updateForcedBindings; private static final Logger logger = LogManager.getLogger("Aggregator"); @@ -50,21 +50,23 @@ public Aggregator(ApplicationProfile appProfile,String subscribeID,String update SPARQL_ID = updateID; sparqlUpdate = appProfile.getSPARQLUpdate(updateID); + + updateForcedBindings = appProfile.getUpdateBindings(updateID); } - public final Response update(Bindings forcedBindings){ - return update(forcedBindings,5000); + public final Response update(){ + return update(0); } - public final Response update(Bindings forcedBindings,int timeout){ - if (protocolClient == null || sparqlUpdate == null) { - logger.fatal("Aggregator not initialized"); - return new ErrorResponse(-1,400,"Aggregator not initialized"); - } - - String sparql = prefixes() + replaceBindings(sparqlUpdate,forcedBindings); - - logger.debug(" "+ SPARQL_ID+" ==> "+sparql); + + public final Response update(int timeout){ + UpdateRequest req = new UpdateRequest(-1,appProfile.getUpdateMethod(SPARQL_ID), appProfile.getUpdateProtocolScheme(SPARQL_ID),appProfile.getUpdateHost(SPARQL_ID), appProfile.getUpdatePort(SPARQL_ID), + appProfile.getUpdatePath(SPARQL_ID), prefixes() + replaceBindings(sparqlUpdate, updateForcedBindings), timeout, + appProfile.getUsingGraphURI(SPARQL_ID), appProfile.getUsingNamedGraphURI(SPARQL_ID)); - return protocolClient.update(new UpdateRequest(sparql),timeout); + return client.update(req); } + + public final void setUpdateBindingValue(String variable, String value) throws IllegalArgumentException { + updateForcedBindings.setBindingValue(variable, value); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java index d7dd11e0..ded7c6b6 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java @@ -96,15 +96,15 @@ "sparql" : "..." , "forcedBindings" : { "variable_1" : { - "type" : "literal" , + "type" : "xsd data type" , "value" : ""} , "variable_2" : { - "type" : "literal" , + "type" : "xsd data type" , "value" : ""} , "variable_N" : { - "type" : "uri" , + "type" : "xsd data type" , "value" : ""} }, "sparql11protocol" :{...} (optional) @@ -120,15 +120,15 @@ "sparql" : "..." , "forcedBindings" : { "variable_1" : { - "type" : "literal" , + "type" : "xsd data type" , "value" : ""} , "variable_2" : { - "type" : "literal" , + "type" : "xsd data type" , "value" : ""} , "variable_N" : { - "type" : "uri" , + "type" : "xsd data type" , "value" : ""} }, "sparql11protocol" :{...} (optional), @@ -170,13 +170,11 @@ public JsonObject getExtendedData() { public boolean isAuthenticationRequiredForUpdate(String id) { try { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().has("authentication"); - } - catch(Exception e) { + } catch (Exception e) { logger.debug(e.getMessage()); - try{ + try { return jsap.has("authentication"); - } - catch (Exception e1) { + } catch (Exception e1) { logger.debug(e1.getMessage()); return false; } @@ -226,16 +224,15 @@ public String getUpdateHost(String id) { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") .getAsJsonObject().get("host").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getHost(); + return super.getDefaultHost(); } public String getUpdateAcceptHeader(String id) { try { if (jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") - .getAsJsonObject().get("format").getAsString().equals("JSON")) + .getAsJsonObject().get("update").getAsJsonObject().get("format").getAsString().equals("JSON")) return "application/json"; else return "application/html"; @@ -249,33 +246,31 @@ public String getUpdateAcceptHeader(String id) { public HTTPMethod getUpdateMethod(String id) { try { if (jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") - .getAsJsonObject().get("method").getAsString().equals("URL_ENCODED_POST")) + .getAsJsonObject().get("update").getAsJsonObject().get("method").getAsString() + .equals("URL_ENCODED_POST")) return HTTPMethod.URL_ENCODED_POST; return HTTPMethod.POST; } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getUpdateMethod(); } - public String getUpdateProtocol(String id) { + public String getUpdateProtocolScheme(String id) { try { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") .getAsJsonObject().get("protocol").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getProtocolScheme(); + return super.getDefaultProtocolScheme(); } public String getUpdatePath(String id) { try { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") - .getAsJsonObject().get("path").getAsString(); + .getAsJsonObject().get("update").getAsJsonObject().get("path").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getUpdatePath(); @@ -286,10 +281,9 @@ public int getUpdatePort(String id) { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") .getAsJsonObject().get("port").getAsInt(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getHttpPort(); + return super.getDefaultPort(); } public String getUsingNamedGraphURI(String id) { @@ -297,7 +291,6 @@ public String getUsingNamedGraphURI(String id) { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() .get("using-named-graph-uri").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getUsingNamedGraphURI(); @@ -308,7 +301,6 @@ public String getUsingGraphURI(String id) { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() .get("using-graph-uri").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getUsingGraphURI(); @@ -320,13 +312,11 @@ public String getUsingGraphURI(String id) { public boolean isAuthenticationRequiredForQuery(String id) { try { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().has("authentication"); - } - catch(Exception e) { + } catch (Exception e) { logger.debug(e.getMessage()); - try{ + try { return jsap.has("authentication"); - } - catch (Exception e1) { + } catch (Exception e1) { logger.debug(e1.getMessage()); return false; } @@ -376,21 +366,19 @@ public String getQueryHost(String id) { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") .getAsJsonObject().get("host").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getHost(); + return super.getDefaultHost(); } - public String getQueryProtocol(String id) { + public String getQueryProtocolScheme(String id) { try { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") .getAsJsonObject().get("protocol").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getProtocolScheme(); + return super.getDefaultProtocolScheme(); } public int getQueryPort(String id) { @@ -398,34 +386,32 @@ public int getQueryPort(String id) { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") .getAsJsonObject().get("port").getAsInt(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getHttpPort(); + return super.getDefaultPort(); } public String getQueryPath(String id) { try { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") - .getAsJsonObject().get("path").getAsString(); + .getAsJsonObject().get("query").getAsJsonObject().get("path").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } - return super.getQueryPath(); + return super.getDefaultQueryPath(); } public HTTPMethod getQueryMethod(String id) { try { if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") - .getAsJsonObject().get("method").getAsString().equals("URL_ENCODED_POST")) + .getAsJsonObject().get("query").getAsJsonObject().get("method").getAsString() + .equals("URL_ENCODED_POST")) return HTTPMethod.URL_ENCODED_POST; else if (jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11protocol") - .getAsJsonObject().get("method").getAsString().equals("GET")) + .getAsJsonObject().get("query").getAsJsonObject().get("method").getAsString().equals("GET")) return HTTPMethod.GET; return HTTPMethod.POST; } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getUpdateMethod(); @@ -455,7 +441,6 @@ public String getNamedGraphURI(String id) { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() .get("named-graph-uri").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getNamedGraphURI(); @@ -466,7 +451,6 @@ public String getDefaultGraphURI(String id) { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("graphs").getAsJsonObject() .get("default-graph-uri").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getDefaultGraphURI(); @@ -482,15 +466,12 @@ public String getSubscribeHost(String id) { protocol = jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") .getAsJsonObject().get("protocol").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); try { return jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject().get("sparql11seprotocol") .getAsJsonObject().get("host").getAsString(); } catch (Exception e1) { - logger.debug(e1.getMessage()); - - return super.getHost(); + return super.getDefaultHost(); } } @@ -502,7 +483,7 @@ public String getSubscribeHost(String id) { logger.debug(e.getMessage()); } - return super.getHost(); + return super.getDefaultHost(); } public int getSubscribePort(String id) { @@ -513,7 +494,6 @@ public int getSubscribePort(String id) { .getAsJsonObject().get("protocol").getAsString()) .getAsJsonObject().get("port").getAsInt(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getSubscribePort(); @@ -527,7 +507,6 @@ public String getSubscribePath(String id) { .getAsJsonObject().get("protocol").getAsString()) .getAsJsonObject().get("path").getAsString(); } catch (Exception e) { - logger.debug(e.getMessage()); } return super.getSubscribePath(); @@ -543,7 +522,6 @@ public SubscriptionProtocol getSubscribeProtocol(String id) { .getAsJsonObject().get("protocol").getAsString().equals("wss")) return SubscriptionProtocol.WSS; } catch (Exception e1) { - logger.debug(e1.getMessage()); } return super.getSubscriptionProtocol(); @@ -581,56 +559,86 @@ public Set getQueryIds() { *
     	 * "forcedBindings" : {
     					"variable_1" : {
    -						"type" : "literal" ,
    -						"value" : ""}
    +						"type" : "xsd:short"}
     					 ,
     					"variable_2" : {
    -						"type" : "literal" ,
    -						"value" : ""}
    +						"type" : "xsd:anyURI"}
     					 ,
    +					 "variable_3" : {
    +					 	"type" : "xsd:string",
    +					 	"language" : "it"},
    +					 ...
     					"variable_N" : {
    -						"type" : "uri" ,
    -						"value" : ""}
    +						"type" : "xsd:dateTime" ,
    +						"value" : "1985-08-03T01:02:03Z"}
     				}
     	 * 
    */ - public Bindings getUpdateBindings(String id) { + public Bindings getUpdateBindings(String id) throws IllegalArgumentException { + if (!jsap.get("updates").getAsJsonObject().has(id)) throw new IllegalArgumentException("Update ID not found"); + Bindings ret = new Bindings(); + if (!jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject() + .has("forcedBindings")) return ret; + try { for (Entry binding : jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject() .get("forcedBindings").getAsJsonObject().entrySet()) { RDFTerm bindingValue = null; - if (binding.getValue().getAsJsonObject().get("type").getAsString().equals("uri")) { - bindingValue = new RDFTermURI(binding.getValue().getAsJsonObject().get("value").getAsString()); + String value = null; + if(binding.getValue().getAsJsonObject().has("value")) value = binding.getValue().getAsJsonObject().get("value").getAsString(); + + String datatype = null; + if (binding.getValue().getAsJsonObject().has("type")) datatype = binding.getValue().getAsJsonObject().get("type").getAsString(); + + if (datatype.equals("xsd:anyURI")) { + bindingValue = new RDFTermURI(value); } else { - bindingValue = new RDFTermLiteral(binding.getValue().getAsJsonObject().get("value").getAsString()); + + String language = null; + if(binding.getValue().getAsJsonObject().has("language")) language = binding.getValue().getAsJsonObject().get("language").getAsString(); + + bindingValue = new RDFTermLiteral(value,datatype,language); } ret.addBinding(binding.getKey(), bindingValue); } } catch (Exception e) { - logger.debug(e.getMessage()); } return ret; } - public Bindings getQueryBindings(String id) { + public Bindings getQueryBindings(String id) throws IllegalArgumentException { + if (!jsap.get("queries").getAsJsonObject().has(id)) throw new IllegalArgumentException("Query ID not found"); + Bindings ret = new Bindings(); + if (!jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject() + .has("forcedBindings")) return ret; + try { for (Entry binding : jsap.get("queries").getAsJsonObject().get(id).getAsJsonObject() .get("forcedBindings").getAsJsonObject().entrySet()) { RDFTerm bindingValue = null; - if (binding.getValue().getAsJsonObject().get("type").getAsString().equals("uri")) { - bindingValue = new RDFTermURI(binding.getValue().getAsJsonObject().get("value").getAsString()); + String value = null; + if(binding.getValue().getAsJsonObject().has("value")) value = binding.getValue().getAsJsonObject().get("value").getAsString(); + + String datatype = null; + if (binding.getValue().getAsJsonObject().has("type")) datatype = binding.getValue().getAsJsonObject().get("type").getAsString(); + + if (datatype.equals("xsd:anyURI")) { + bindingValue = new RDFTermURI(value); } else { - bindingValue = new RDFTermLiteral(binding.getValue().getAsJsonObject().get("value").getAsString()); + + String language = null; + if(binding.getValue().getAsJsonObject().has("language")) language = binding.getValue().getAsJsonObject().get("language").getAsString(); + + bindingValue = new RDFTermLiteral(value,datatype,language); } ret.addBinding(binding.getKey(), bindingValue); } } catch (Exception e) { - logger.debug(e.getMessage()); } return ret; @@ -677,14 +685,14 @@ public String getUpdateUrl(String id) { String port = ""; if (getUpdatePort(id) != -1) port = ":" + getUpdatePort(id); - return getUpdateProtocol(id) + "://" + getUpdateHost(id) + port + getUpdatePath(id); + return getUpdateProtocolScheme(id) + "://" + getUpdateHost(id) + port + getUpdatePath(id); } public String getQueryUrl(String id) { String port = ""; if (getQueryPort(id) != -1) port = ":" + getQueryPort(id); - return getQueryProtocol(id) + "://" + getQueryHost(id) + port + getQueryPath(id); + return getQueryProtocolScheme(id) + "://" + getQueryHost(id) + port + getQueryPath(id); } public String getSubscribeUrl(String id) { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java index 7c69413f..2241fa93 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java @@ -25,15 +25,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -public abstract class Client { - private final Logger logger = LogManager.getLogger("Client"); +public abstract class Client implements java.io.Closeable { + protected final Logger logger = LogManager.getLogger(); protected ApplicationProfile appProfile; - protected SPARQL11SEProtocol protocolClient = null; protected String prefixes = ""; public ApplicationProfile getApplicationProfile() { @@ -77,9 +75,14 @@ protected String replaceBindings(String sparql, Bindings bindings){ if (bindings.getBindingValue(var) == null) continue; String value = bindings.getBindingValue(var); + if (value == null) continue; //Use single quote "'" so that the literal value can contain also double quotes """ - if (bindings.isLiteral(var)) value = "'"+value+"'"; + if (bindings.isLiteral(var)) { + value = "'"+value+"'"; + if (bindings.getLanguage(var)!=null) value += "@"+bindings.getLanguage(var); + else if (bindings.getDatatype(var) != null) value += "^^" + bindings.getDatatype(var); + } else { // See https://www.w3.org/TR/rdf-sparql-query/#QSynIRI // https://docs.oracle.com/javase/7/docs/api/java/net/URI.html diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java index a4e41a8f..e6f51140 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java @@ -18,29 +18,36 @@ package it.unibo.arces.wot.sepa.pattern; +import java.io.IOException; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; +import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; public abstract class Consumer extends Client implements IConsumer { private static final Logger logger = LogManager.getLogger("Consumer"); - + protected String sparqlSubscribe = null; protected String subID = ""; + private Bindings forcedBindings; - public Consumer(ApplicationProfile appProfile, String subscribeID) throws SEPAProtocolException, SEPASecurityException { + protected SPARQL11SEProtocol client; + + public Consumer(ApplicationProfile appProfile, String subscribeID) + throws SEPAProtocolException, SEPASecurityException { super(appProfile); if (subscribeID == null) { @@ -55,43 +62,54 @@ public Consumer(ApplicationProfile appProfile, String subscribeID) throws SEPAPr } sparqlSubscribe = appProfile.getSPARQLQuery(subscribeID); - - protocolClient = new SPARQL11SEProtocol(appProfile,this); - } - public final Response subscribe(Bindings forcedBindings){ + forcedBindings = appProfile.getQueryBindings(subscribeID); + if (sparqlSubscribe == null) { - logger.fatal("SPARQL SUBSCRIBE not defined"); - return null; + logger.fatal("SPARQL subscribe is null"); + throw new SEPAProtocolException(new IllegalArgumentException("SPARQL subscribe is null")); } - if (protocolClient == null) { - logger.fatal("Client not initialized"); - return null; + ISubscriptionProtocol protocol = null; + switch (appProfile.getSubscribeProtocol(subscribeID)) { + case WS: + protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), + appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID), false); + break; + case WSS: + protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), + appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID), true); + break; } + client = new SPARQL11SEProtocol(appProfile,protocol, this); + } + + public final void setSubscribeBindingValue(String variable, String value) throws IllegalArgumentException { + forcedBindings.setBindingValue(variable, value); + + } + public final Response subscribe() { String sparql = prefixes() + replaceBindings(sparqlSubscribe, forcedBindings); - logger.debug(" ==> " + sparql); + Response response = client.subscribe(new SubscribeRequest(sparql)); - Response response = protocolClient.subscribe(new SubscribeRequest(sparql)); - if (response.isSubscribeResponse()) { - subID = ((SubscribeResponse)response).getSpuid(); + subID = ((SubscribeResponse) response).getSpuid(); } - + return response; } public final Response unsubscribe() { logger.debug("UNSUBSCRIBE " + subID); - if (protocolClient == null) { - logger.fatal("Client not initialized"); - return new ErrorResponse(-1,400,"Client not initialized"); - } + return client.unsubscribe(new UnsubscribeRequest(subID)); + } - return protocolClient.unsubscribe(new UnsubscribeRequest(subID)); + @Override + public void close() throws IOException { + client.close(); } @Override @@ -106,6 +124,6 @@ public final void onSemanticEvent(Notification notify) { onAddedResults(added); if (!removed.isEmpty()) onRemovedResults(removed); - onResults(results); + onResults(results); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java index fe6e8fd1..bcd8ece8 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java @@ -18,92 +18,170 @@ package it.unibo.arces.wot.sepa.pattern; +import java.io.IOException; +import java.util.Hashtable; + import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; public class GenericClient extends Client { - public GenericClient(ApplicationProfile appProfile,ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException { + private Hashtable subscribedClients = new Hashtable(); + private Hashtable subscriptions = new Hashtable(); + + public GenericClient(ApplicationProfile appProfile) throws SEPAProtocolException, SEPASecurityException { super(appProfile); - protocolClient = new SPARQL11SEProtocol(appProfile, handler); } - public Response update(String ID,String SPARQL_UPDATE, Bindings forced,String usingGraphUri,String usingNamedGraphUri) throws SEPAProtocolException, SEPASecurityException { - protocolClient = new SPARQL11SEProtocol(appProfile,ID,true); - // TODO: move default timeout in jsap - UpdateRequest req = new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)); - // Graphs - req.setUsingGraphUri(usingGraphUri); - req.setNamedGraphUri(usingNamedGraphUri); - // Authentication - if (appProfile.isAuthenticationRequiredForUpdate(SPARQL_UPDATE)) { - - } - return protocolClient.update(req,5000); - } - - public Response update(String ID,String SPARQL_UPDATE, Bindings forced,int timeout) throws SEPAProtocolException, SEPASecurityException { - protocolClient = new SPARQL11SEProtocol(appProfile,ID,true); - return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)),timeout); - } - - public Response update(String SPARQL_UPDATE, Bindings forced,int timeout) { - return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)),timeout); - } - public Response update(String SPARQL_UPDATE, Bindings forced) { - // TODO: move default timeout in jsap - return protocolClient.update(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced)),5000); - } + public Response update(String ID, String SPARQL_UPDATE, Bindings forced, String usingGraphUri, + String usingNamedGraphUri, HTTPMethod method, int timeout) + throws SEPAProtocolException, SEPASecurityException, IOException { - public Response secureUpdate(String SPARQL_UPDATE, Bindings forced) { - return protocolClient.secureUpdate(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced))); - } + UpdateRequest req = new UpdateRequest(-1,method, appProfile.getUpdateProtocolScheme(ID), + appProfile.getUpdateHost(ID), appProfile.getUpdatePort(ID), appProfile.getUpdatePath(ID), + prefixes() + replaceBindings(SPARQL_UPDATE, forced), timeout, usingGraphUri, usingNamedGraphUri); - public Response query(String SPARQL_QUERY, Bindings forced) { - return protocolClient.query(new QueryRequest(prefixes() + replaceBindings(SPARQL_QUERY, forced))); - } + SPARQL11Protocol client = new SPARQL11Protocol(); + Response ret = client.update(req); + client.close(); - public Response secureQuery(String SPARQL_QUERY, Bindings forced) { - return protocolClient.secureQuery(new QueryRequest(prefixes() + replaceBindings(SPARQL_QUERY, forced))); + return ret; } - public Response subscribe(String SPARQL_SUBSCRIBE, Bindings forced) { - return protocolClient.subscribe(new SubscribeRequest(prefixes() + replaceBindings(SPARQL_SUBSCRIBE, forced))); + public Response query(String ID, String SPARQL_QUERY, Bindings forced, String defaultGraphUri, String namedGraphUri, + HTTPMethod method, int timeout) throws SEPAProtocolException, SEPASecurityException, IOException { + + QueryRequest req = new QueryRequest(-1,method, appProfile.getQueryProtocolScheme(ID), appProfile.getQueryHost(ID), + appProfile.getQueryPort(ID), appProfile.getQueryPath(ID), + prefixes() + replaceBindings(SPARQL_QUERY, forced), timeout, defaultGraphUri, namedGraphUri); + + SPARQL11Protocol client = new SPARQL11Protocol(); + Response ret = client.query(req); + client.close(); + + return ret; } - public Response secureSubscribe(String SPARQL_SUBSCRIBE, Bindings forced) { - return protocolClient - .secureSubscribe(new SubscribeRequest(prefixes() + replaceBindings(SPARQL_SUBSCRIBE, forced))); + public Response subscribe(String ID, String SPARQL_SUBSCRIBE, Bindings forced, String defaultGraphUri, + String namedGraphUri, ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException { + + SubscribeRequest req = new SubscribeRequest(prefixes() + replaceBindings(SPARQL_SUBSCRIBE, forced)); + + String url; + if (appProfile.getSubscribeProtocol(ID).equals(SubscriptionProtocol.WS)) { + url = "ws_" + appProfile.getSubscribeHost(ID) + "_" + appProfile.getSubscribePort(ID) + "_" + + appProfile.getSubscribePath(ID); + } else { + url = "wss_" + appProfile.getSubscribeHost(ID) + "_" + appProfile.getSubscribePort(ID) + "_" + + appProfile.getSubscribePath(ID); + } + if (!subscribedClients.containsKey(url)) { + ISubscriptionProtocol protocol = null; + switch (appProfile.getSubscribeProtocol(ID)) { + case WS: + protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), + appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID), false); + break; + case WSS: + protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), + appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID), true); + break; + } + + subscribedClients.put(url,new SPARQL11SEProtocol(appProfile,protocol, handler)); + } + + Response ret = subscribedClients.get(url).subscribe(req); + + if (ret.isSubscribeResponse()) + subscriptions.put(((SubscribeResponse) ret).getSpuid(), subscribedClients.get(url)); + + return ret; } public Response unsubscribe(String subID) { - return protocolClient.unsubscribe(new UnsubscribeRequest(subID)); - } + if (!subscriptions.contains(subID)) + return new ErrorResponse(400, subID + " not present"); - public Response secureUnsubscribe(String subID) { - return protocolClient.secureUnsubscribe(new UnsubscribeRequest(subID)); + return subscriptions.get(subID).unsubscribe(new UnsubscribeRequest(subID)); } - // Registration to the Authorization Server (AS) - public Response register(String identity) { - return protocolClient.register(identity); + @Override + public void close() throws IOException { + for (SPARQL11SEProtocol client : subscribedClients.values()) + client.close(); } - // Token request to the Authorization Server (AS) - public Response requestToken() { - return protocolClient.requestToken(); - } - - // Retrieve the token expiring seconds - public long getTokenExpiringSeconds() throws SEPASecurityException { - return protocolClient.getTokenExpiringSeconds(); - } +// public Response secureUpdate(String SPARQL_UPDATE, Bindings forced) { +// return protocolClient.secureUpdate(new UpdateRequest(prefixes() + replaceBindings(SPARQL_UPDATE, forced))); +// } +// +// public Response query(String SPARQL_QUERY, Bindings forced) { +// return protocolClient.query(new QueryRequest(prefixes() + replaceBindings(SPARQL_QUERY, forced))); +// } +// +// public Response secureQuery(String SPARQL_QUERY, Bindings forced) { +// return protocolClient.secureQuery(new QueryRequest(prefixes() + replaceBindings(SPARQL_QUERY, forced))); +// } +// +// public Response secureSubscribe(String SPARQL_SUBSCRIBE, Bindings forced) { +// return protocolClient +// .secureSubscribe(new SubscribeRequest(prefixes() + replaceBindings(SPARQL_SUBSCRIBE, forced))); +// } +// +// public Response secureUnsubscribe(String subID) { +// return protocolClient.secureUnsubscribe(new UnsubscribeRequest(subID)); +// } + +// // Registration to the Authorization Server (AS) +// public Response register(String identity) { +// SPARQL11SEProtocol client = new SPARQL11SEProtocol(); +// Response ret = client.register(identity); +// try { +// client.close(); +// } catch (IOException e) { +// logger.warn(e.getMessage()); +// } +// return ret; +// } +// +// // Token request to the Authorization Server (AS) +// public Response requestToken() { +// SPARQL11SEProtocol client = new SPARQL11SEProtocol(); +// Response ret = client.requestToken(); +// try { +// client.close(); +// } catch (IOException e) { +// logger.warn(e.getMessage()); +// } +// return ret; +// } +// +// // Retrieve the token expiring seconds +// public long getTokenExpiringSeconds() throws SEPASecurityException { +// SPARQL11SEProtocol client = new SPARQL11SEProtocol(); +// long ret = client.getTokenExpiringSeconds(); +// try { +// client.close(); +// } catch (IOException e) { +// logger.warn(e.getMessage()); +// } +// return ret; +// } + } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java index f9fba219..844a3909 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java @@ -22,11 +22,10 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; public interface IConsumer extends ISubscriptionHandler { - Response subscribe(Bindings forcedBindings) ; + Response subscribe() ; Response unsubscribe(); void onResults(ARBindingsResults results); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java index 6ed13ea3..9853b142 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java @@ -19,8 +19,8 @@ package it.unibo.arces.wot.sepa.pattern; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; public interface IProducer { - public Response update(Bindings forcedBindings); + public Response update(); + public Response update(int timeout); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java index 1855134b..be3da2d7 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java @@ -18,23 +18,28 @@ package it.unibo.arces.wot.sepa.pattern; +import java.io.IOException; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; public class Producer extends Client implements IProducer { protected String sparqlUpdate = null; protected String SPARQL_ID = ""; + private Bindings forcedBindings; private static final Logger logger = LogManager.getLogger("Producer"); - public Producer(ApplicationProfile appProfile,String updateID) throws SEPAProtocolException { + private final SPARQL11Protocol client = new SPARQL11Protocol(); + + public Producer(ApplicationProfile appProfile,String updateID) throws SEPAProtocolException, SEPASecurityException { super(appProfile); if (appProfile.getSPARQLUpdate(updateID) == null) { @@ -46,24 +51,28 @@ public Producer(ApplicationProfile appProfile,String updateID) throws SEPAProtoc sparqlUpdate = appProfile.getSPARQLUpdate(updateID); - protocolClient = new SPARQL11SEProtocol(appProfile); + forcedBindings = appProfile.getUpdateBindings(updateID); } - public Response update(Bindings forcedBindings) { - //TODO : move default timeout in jsap - return update(forcedBindings,5000); + public final Response update() { + return update(0); } - public Response update(Bindings forcedBindings,int timeout){ - if (sparqlUpdate == null || protocolClient == null) { - logger.fatal("Producer not initialized"); - return new ErrorResponse(-1,400,"Producer not initialized"); - } - - String sparql = prefixes() + replaceBindings(sparqlUpdate,forcedBindings); + public final Response update(int timeout){ + UpdateRequest req = new UpdateRequest(-1,appProfile.getUpdateMethod(SPARQL_ID), appProfile.getUpdateProtocolScheme(SPARQL_ID),appProfile.getUpdateHost(SPARQL_ID), appProfile.getUpdatePort(SPARQL_ID), + appProfile.getUpdatePath(SPARQL_ID), prefixes() + replaceBindings(sparqlUpdate, forcedBindings), timeout, + appProfile.getUsingGraphURI(SPARQL_ID), appProfile.getUsingNamedGraphURI(SPARQL_ID)); - logger.debug(" "+ SPARQL_ID+" ==> "+sparql); - - return protocolClient.update(new UpdateRequest(sparql),timeout); + return client.update(req); } + + @Override + public void close() throws IOException { + client.close(); + } + + public final void setUpdateBindingValue(String variable, String value) throws IllegalArgumentException { + forcedBindings.setBindingValue(variable, value); + + } } diff --git a/client-api/src/main/resources/log4j2.xml b/client-api/src/main/resources/log4j2.xml index f0b0d77e..3be39197 100644 --- a/client-api/src/main/resources/log4j2.xml +++ b/client-api/src/main/resources/log4j2.xml @@ -1,17 +1,31 @@ - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index 6a9aad79..e55bf637 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -1,21 +1,20 @@ package it.unibo.arces.wot.sepa.api; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import java.io.File; import java.net.URL; public class ConfigurationProvider { - public static SPARQL11SEProperties GetTestEnvConfiguration() throws SEPAPropertiesException { - SPARQL11SEProperties result; + public static ApplicationProfile GetTestEnvConfiguration() throws SEPAPropertiesException { + ApplicationProfile result; final String configuaration = System.getProperty("testConfiguration"); if( configuaration != null){ - final File confFile = new File(configuaration); - result = new SPARQL11SEProperties(confFile); + result = new ApplicationProfile(configuaration); }else{ URL config = Thread.currentThread().getContextClassLoader().getResource("dev.jsap"); - result = new SPARQL11SEProperties(new File(config.getPath())); + result = new ApplicationProfile(config.getPath()); } return result; } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java index 9b433688..355acda8 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java @@ -1,11 +1,14 @@ package it.unibo.arces.wot.sepa.api; +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; +import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -22,9 +25,21 @@ public class ITProtocolTest { @Before public void setUp() throws Exception { - final SPARQL11SEProperties properties = ConfigurationProvider.GetTestEnvConfiguration(); + final ApplicationProfile properties = ConfigurationProvider.GetTestEnvConfiguration(); subHandler = new MockSubscriptionHandler(); - client = new SPARQL11SEProtocol(properties,subHandler); + + ISubscriptionProtocol protocol = null; + switch (properties.getSubscribeProtocol(null)) { + case WS: + protocol = new WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), + properties.getSubscribePort(null), properties.getSubscribePath(null), false); + break; + case WSS: + protocol = new WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), + properties.getSubscribePort(null), properties.getSubscribePath(null), true); + break; + } + client = new SPARQL11SEProtocol(properties,protocol, subHandler); } @After @@ -86,12 +101,13 @@ public void VerifiedUTF8Update(){ private static Response SubmitQuery(SPARQL11SEProtocol client,String query) { final QueryRequest queryRequest = new QueryRequest(query); - return client.query(queryRequest,5000); + + return client.query(queryRequest); } private static Response SubmitUpdate(SPARQL11SEProtocol client,String query) { final UpdateRequest updateRequest = new UpdateRequest(query); - return client.update(updateRequest,5000); + return client.update(updateRequest); } private static Response submitSubscribe(String query, SPARQL11SEProtocol client) { diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java index 3ed0389d..8a10aff9 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java @@ -1,12 +1,14 @@ package it.unibo.arces.wot.sepa.api; +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import java.io.File; import java.net.URL; import static org.junit.Assert.*; @@ -17,14 +19,26 @@ public class ITSecureProtocolTest { private final static String VALID_ID = "SEPATest"; private final static String NOT_VALID_ID = "RegisterMePlease"; - private SPARQL11SEProperties properties; + private ApplicationProfile properties; @Before public void setUp() throws Exception { URL config = Thread.currentThread().getContextClassLoader().getResource("dev.jsap"); - properties = new SPARQL11SEProperties(new File(config.getPath())); + properties = new ApplicationProfile(config.getPath()); subHandler = new MockSubscriptionHandler(); - client = new SPARQL11SEProtocol(properties,subHandler); + + ISubscriptionProtocol protocol = null; + switch (properties.getSubscribeProtocol(null)) { + case WS: + protocol = new WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), + properties.getSubscribePort(null), properties.getSubscribePath(null), false); + break; + case WSS: + protocol = new WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), + properties.getSubscribePort(null), properties.getSubscribePath(null), true); + break; + } + client = new SPARQL11SEProtocol(properties,protocol, subHandler); } @Test diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java index 4bb97b08..d857cd36 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java @@ -21,7 +21,7 @@ public void onSemanticEvent(Notification notify) { @Override - public void onBrokenSocket() { + public void onBrokenConnection() { pingSemaphore.release(); notSemaphore.release(); } diff --git a/client-pac-pattern/LICENSE b/client-pac-pattern/LICENSE deleted file mode 100644 index 65c5ca88..00000000 --- a/client-pac-pattern/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/client-pac-pattern/defaults.jpar b/client-pac-pattern/defaults.jpar deleted file mode 100644 index 3ab2d892..00000000 --- a/client-pac-pattern/defaults.jpar +++ /dev/null @@ -1 +0,0 @@ -{"parameters":{"host":"localhost","paths":{"query":"/query","update":"/update","subscribe":"/subscribe","register":"/oauth/register","tokenRequest":"/oauth/token","securePath":"/secure"},"methods":{"query":"POST","update":"URL_ENCODED_POST"},"formats":{"query":"JSON","update":"HTML"}}} \ No newline at end of file diff --git a/client-pac-pattern/pom.xml b/client-pac-pattern/pom.xml deleted file mode 100644 index 0e6b15d0..00000000 --- a/client-pac-pattern/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ - - 4.0.0 - - it.unibo.arces.wot - sepa - ${revision} - - client-pac-pattern - - - - org.apache.logging.log4j - log4j-api - 2.8.1 - - - - - org.apache.logging.log4j - log4j-core - 2.8.1 - - - - - com.google.code.gson - gson - 2.8.0 - - - - it.unibo.arces.wot - commons - ${revision} - - - it.unibo.arces.wot - client-api - ${revision} - - - - diff --git a/client-pac-pattern/src/main/resources/defaults.jpar b/client-pac-pattern/src/main/resources/defaults.jpar deleted file mode 100644 index b3f68a98..00000000 --- a/client-pac-pattern/src/main/resources/defaults.jpar +++ /dev/null @@ -1 +0,0 @@ -{"parameters":{"host":"localhost","ports":{"http":9999,"https":8443,"ws":9000,"wss":9443},"paths":{"query":"/blazegraph/namespace/kb/sparql","update":"/blazegraph/namespace/kb/sparql","subscribe":"/subscribe","register":"/oauth/register","tokenRequest":"/oauth/token","securePath":"/secure"},"methods":{"query":"POST","update":"URL_ENCODED_POST"},"formats":{"query":"JSON","update":"HTML"}}} \ No newline at end of file diff --git a/client-pac-pattern/src/main/resources/log4j2.xml b/client-pac-pattern/src/main/resources/log4j2.xml deleted file mode 100644 index f3889cbb..00000000 --- a/client-pac-pattern/src/main/resources/log4j2.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/client-pac-pattern/src/main/resources/sepa.jks b/client-pac-pattern/src/main/resources/sepa.jks deleted file mode 100644 index 266d030c47853b8b2ed15feb1d51bd3dcb7a92ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3103 zcmd6p`9G9v1IA~?jD4B0)MV^Q#xrA$H;g4|Qiv>B4rVa2#h7rkj4eynNcKb{kwex* zDO|+;d-9y^L+06dhYLa-`mUE%ODU4^3MQ+I0GsE zE@vqrAQ0H0;6d;w5C{$d!eQG09|uy20}KNnfCz%Ya1bmUw)p1m+^66{wQD8vtbHeG z8Rz=MhtSrokz%d$ZicJer+Z$`OLdlyD+t0HjhE&MzNI^uk(%d41Riy9@3&OWRjHG` z@t0Rd!RJ|#wGE{r%ea>#eM3^Tl=@4x6+0Tek;KZry<_N4TrS;hjwdgj0{4a%a@7B+ zwk3IbW`7DRK2DqLYP1qt(?s(GIe}!~pK(^;L6qoH*?xii|JZpSir;w`G?lLH-JjN~npP-Y9mrh9LhPZ!vkZ^d{C7?_ZskkjKV z20qp>+vE&3%TH^qJ2yIfp^|E!NbFB|Qh+U9PHaFO-f@tK4YwDPIkoAEuvh&|tkxQghak}a2T}Yr9OJmiSnhEn4Z0{^O?(3an|HSKcnsY z!{fcF@>#h$5(^Lp(J2%%ggc`FuWQ4dqk66^ycZ$ge|zDthtc(+$O5=;xgE4Qw?CzI zy|`P8$&$Z<5?;v=bY0DgEM8w`U?~#I`=gH^sX;~e=q}okOw=J2UL#=|TbifM92`rV z&u7HRCf8V$mPf@8dr$FRd#cX!9~xP>K#FT7tskEtC|bfg&C*OTo5DzU=iZka-J{a6 zeZ2F^O?p;p*bNKQMt=_vykwMyLSP5^)e|}RcO<4c0acvcU&M*;|8ANyr4Rwc+?5`4 zeX7SJsv4q9d(asUZF6bSo4(H{Md_4h`FfOAD(Vs=I6RjpYuW)uRKp0%TI&rq}pSd@qhovP-*bo zS_ET%R3t-~83CvjH{5yf@%5Q_d#KNN^rkyiqw_@sH+a?T-zODhIm1 z%o$ahe|g5V4eNKMKtEA zo%t%-15Fj9v5K>9dCmJ*>mv!hIFw{n@ z&zsP5ZjR!|^rDVhM2|P!gJ82bt+}^M2}VVyP$377X~SyUVa4l4%E-7g}|3Y_g<`$Ld6o8XJ_;rip6(TNY3lh4{pM#AI+jrn)T z?0N));I=q*;1CD|Z2`iekAZMV@l7ZM41vHDM@@zSUJk^8=%dkJc)?(9I0#@Q;kf|> z2hqFkx zcjQ z-<&6LdDed9>xn+xK_kXv$F%cDzU4?YMOi)Qb_|z%zZ!B+>wH8;pQ**g6(D2a9=hEs zwI%D`o4cJ2^5zYFpP2_)Ww-`4hl^J=`4^vGt@E@QplhI^wj&b;T8yy@%Q+FZ9`uBL#BQ2}2Y8|f0 z4Xv5>1vKh14G=rMrLY=Y?K5{v;l#qe=H=!MTgG+XLOIjf%=St${lmriXY*obD`I+P zLe6AZ_Yj;ahf%|!hq@&um%j8U%^th}TJiqM>EUOqGdaOZv1ZOy7jw5e4b_Lk(vIo| ziP3_{6Ya|hmS^4%dZc`dU+zzDp1QJPcAIESZT;ARpGnLjn|25hRK{hZM3od2{7Dc+ zR@nHFUV^x|id@*JxWZrATIO4m{43UC7siJy)SXtB2j`|(R!H^6^SaNZlGa#1E7%9DSi+{6Gc3ZQ;a&ckQz z%Lb2(C9yFi`}zEWo0A7_L+{Qi^9x{~bL>|5RWt}NS|GsBgA$E2z?=YR5DpOtc)a={Hs*v~srirm zH%^(6|9h^xm@^XC*~UT`3{bl)jeIH~Kzp_%wX#cAli5Ow0HNXy67?uBXaJ_HdZY xY8|C6bHM&_dCtPtp5+Da5ucdedYDmKMoF3yv9xSr*l?!LUVjMJP7Kam{V#KL`vw32 diff --git a/client-pac-pattern/src/test/resources/chat.jsap b/client-pac-pattern/src/test/resources/chat.jsap deleted file mode 100644 index 78381f0d..00000000 --- a/client-pac-pattern/src/test/resources/chat.jsap +++ /dev/null @@ -1,98 +0,0 @@ -{ - "parameters" : { - "host" : "mml.arces.unibo.it" , - "ports" : { - "http" : 8000 , - "https" : 8443 , - "ws" : 9000 , - "wss" : 9443} - , - "paths" : { - "query" : "/query" , - "update" : "/update" , - "subscribe" : "/subscribe" , - "register" : "/oauth/register" , - "tokenRequest" : "/oauth/token" , - "securePath" : "/secure"} - , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} - , - "security" : { - "client_id" : "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs" , - "client_secret" : "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg" , - "jwt" : "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT" , - "expires" : "04/5tRBT5n/VJ0XQASgs/w==" , - "type" : "XPrHEX2xHy+5IuXHPHigMw=="} - } - , - "namespaces" : { - "chat" : "http://wot.arces.unibo.it/chat#" , - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#"} - , - "updates" : { - "SEND_MESSAGE" : { - "sparql" : "INSERT {?message rdf:type chat:Message ; chat:text ?text ; chat:from ?sender ; chat:to ?receiver; chat:time ?time} WHERE { BIND(now() AS ?time) BIND(IRI(CONCAT(\"chat:Message-\",STRUUID())) AS ?message)}" , - "forcedBindings" : { - "text" : { - "type" : "literal" , - "value" : "Hello!"} - , - "sender" : { - "type" : "literal" , - "value" : "Me"} - , - "receiver" : { - "type" : "literal" , - "value" : "You"} - } - } - , - "DELETE_MESSAGES" : { - "sparql" : "DELETE {?message rdf:type chat:Message ; chat:text ?text ; chat:from ?sender ; chat:to ?receiver; chat:time ?time} WHERE {?message rdf:type chat:Message ; chat:text ?text ; chat:from ?sender ; chat:to ?receiver; chat:time ?time}" , - "forcedBindings" : { - "receiver" : { - "type" : "literal" , - "value" : ""} - } - }, - "REGISTER_USER" : { - "sparql": "INSERT {?id rdf:type chat:User ; chat:userName ?userName} WHERE {BIND(IRI(CONCAT(\"chat:User-\",STRUUID())) AS ?id) . FILTER NOT EXISTS {?x rdf:type chat:User . ?x chat:userName ?userName}}", - "forcedBindings" : { - "userName" : { - "type" : "literal" , - "value" : ""} - } - }, - "DELETE_ALL" : {"sparql" : "delete {?s ?p ?o} where {?s ?p ?o}"} - } - , - "queries" : { - "RECEIVE_MESSAGE" : { - "sparql" : "SELECT ?sender ?text ?time WHERE {?message rdf:type chat:Message ; chat:text ?text ; chat:from ?sender ; chat:to ?receiver; chat:time ?time} ORDER BY ?time" , - "forcedBindings" : { - "receiver" : { - "type" : "literal" , - "value" : ""} - } - } - , - "SEND_MESSAGE" : { - "sparql" : "SELECT ?receiver ?text ?time WHERE {?message rdf:type chat:Message ; chat:text ?text ; chat:from ?sender ; chat:to ?receiver; chat:time ?time} ORDER BY ?time" , - "forcedBindings" : { - "sender" : { - "type" : "literal" , - "value" : ""} - } - }, - "USERS" : { - "sparql" : "SELECT ?userName WHERE {?user rdf:type chat:User ; chat:userName ?userName}" - }, - "QUERY_ALL" : {"sparql" : "select * where {?s ?p ?o}"} - } -} diff --git a/client-pac-pattern/src/test/resources/habitat.jsap b/client-pac-pattern/src/test/resources/habitat.jsap deleted file mode 100644 index aeb38de7..00000000 --- a/client-pac-pattern/src/test/resources/habitat.jsap +++ /dev/null @@ -1,92 +0,0 @@ -{ - "parameters": { - "host": "localhost", - "ports": { - "http": 8000, - "https": 8443, - "ws": 9000, - "wss": 9443 - }, - "paths": { - "query": "/query", - "update": "/update", - "subscribe": "/subscribe", - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure" - }, - "methods": { - "query": "POST", - "update": "URL_ENCODED_POST" - }, - "formats": { - "query": "JSON", - "update": "HTML" - }, - "security": { - "client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs", - "client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT", - "expires": "04/5tRBT5n/VJ0XQASgs/w==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - } - }, - "namespaces": { - "hbt": "http://it.unibo/habitat#", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - }, - "updates": { - "INSERT_USER_POSITION_EVENT" : { - "sparql" : "INSERT {?userPositionEvent rdf:type hbt:UserPositionEvent ; hbt:typeMsg ?typeMsg ; hbt:idReader ?idReader ; hbt:idActiveTag ?idActiveTag ; hbt:posX ?posX ; hbt:posY ?posY ; hbt:timestamp ?timestamp } WHERE{ BIND(IRI(CONCAT(\"http://it.unibo/habitat#UserPositionEvent-\",STRUUID())) AS ?userPositionEvent) BIND(now() AS ?timestamp)}", - "forcedBindings" : { - "typeMsg" : { - "type" : "literal" , - "value" : "position"} - , - "idReader" : { - "type" : "literal" , - "value" : "READER_PIPPO"} - , - "idActiveTag" : { - "type" : "literal" , - "value" : "TAG_LUCA"} - , - "posX" : { - "type" : "literal" , - "value" : "100"} - , - "posY" : { - "type" : "literal" , - "value" : "99"} - } - }, - - "DELETE_USER_POSITION_EVENT" : { - "sparql" : "DELETE {?s ?p ?o} WHERE {?s rdf:type hbt:UserPositionEvent . ?s ?p ?o}", - "forcedBindings" : { - "userPositionEvent" : { - "type" : "uri" , - "value" : ""} - } - }, - "DELETE_ALL_USER_POSITION_EVENTS" : { - "sparql" : "DELETE {?s ?p ?o} WHERE { ?s rdf:type hbt:UserPositionEvent ; hbt:typeMsg ?typeMsg ; ?p ?o}", - "forcedBindings" : { - "typeMsg" : { - "type" : "literal" , - "value" : "position"} - } - } - - }, - "queries" : { - "SELECT_ALL_USER_POSITION_EVENTS" : { - "sparql" : "SELECT ?userPositionEvent ?typeMsg ?idReader ?idActiveTag ?posX ?posY ?timestamp WHERE{?userPositionEvent rdf:type hbt:UserPositionEvent ; hbt:typeMsg ?typeMsg ; hbt:idReader ?idReader ; hbt:idActiveTag ?idActiveTag ; hbt:posX ?posX ; hbt:posY ?posY ; hbt:timestamp ?timestamp }", - "forcedBindings" : { - "typeMsg" : { - "type" : "literal" , - "value" : "position"} - } - } - } -} diff --git a/client-pac-pattern/src/test/resources/log4j2.xml b/client-pac-pattern/src/test/resources/log4j2.xml deleted file mode 100644 index f3889cbb..00000000 --- a/client-pac-pattern/src/test/resources/log4j2.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/client-wot-framework/pom.xml b/client-wot-framework/pom.xml deleted file mode 100644 index 4acd16c9..00000000 --- a/client-wot-framework/pom.xml +++ /dev/null @@ -1,34 +0,0 @@ - - 4.0.0 - - wot-api - 0.0.1-SNAPSHOT - jar - - wot-api - http://maven.apache.org - - - UTF-8 - - - - - junit - junit - 3.8.1 - test - - - it.unibo.arces.wot - client-pac-pattern - ${revision} - - - - it.unibo.arces.wot - sepa - ${revision} - - diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/ThingDescription.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/ThingDescription.java deleted file mode 100644 index 659b0462..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/ThingDescription.java +++ /dev/null @@ -1,191 +0,0 @@ -package it.unibo.arces.wot.framework; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class ThingDescription { - private ApplicationProfile app; - - private PropertyPublisher properties; - private ActionPublisher actions; - private ActionWithInputPublisher actionsWithInput; - private EventPublisher events; - private EventWithOutputPublisher eventsWithOutput; - private PropertyChangeEventPublisher propertyChangeEvents; - private TargetPropertyPublisher targetProperties; - private ProtocolPublisher protocols; - private ThingDescriptionPublisher thingDescription; - - private RDFTermURI thing; - - public ThingDescription(ApplicationProfile app,String thingURI,String name) throws SEPAProtocolException, SEPASecurityException { - - this.app = app; - this.properties = new PropertyPublisher(); - this.actions = new ActionPublisher(); - this.actionsWithInput = new ActionWithInputPublisher(); - this.events = new EventPublisher(); - this.eventsWithOutput = new EventWithOutputPublisher(); - this.propertyChangeEvents = new PropertyChangeEventPublisher(); - this.targetProperties = new TargetPropertyPublisher(); - this.thingDescription = new ThingDescriptionPublisher(); - - Bindings bind = new Bindings(); - thing = new RDFTermURI(thingURI); - bind.addBinding("thing", thing); - bind.addBinding("name", new RDFTermLiteral(name)); - thingDescription.update(bind); - } - - public ThingDescription(String thingURI,String name) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - this(new ApplicationProfile("td.jsap"),thingURI,name); - } - - public void addProperty(String property,String name,String dataType,String stability,String writable,String value){ - Bindings bind = new Bindings(); - bind.addBinding("thing", thing); - bind.addBinding("property", new RDFTermURI(property)); - bind.addBinding("name", new RDFTermLiteral(name)); - bind.addBinding("stability", new RDFTermLiteral(stability)); - bind.addBinding("writable", new RDFTermLiteral(writable)); - bind.addBinding("dataType", new RDFTermURI(dataType)); - bind.addBinding("value", new RDFTermLiteral(value)); - properties.update(bind); - } - - public void addAction(String action,String name,String protocol){ - Bindings bind = new Bindings(); - bind.addBinding("thing", thing); - bind.addBinding("action", new RDFTermURI(action)); - bind.addBinding("name", new RDFTermLiteral(name)); - bind.addBinding("protocol", new RDFTermURI(protocol)); - actions.update(bind); - } - - public void addAction(String action,String name,String protocol,String dataType){ - Bindings bind = new Bindings(); - bind.addBinding("thing", thing); - bind.addBinding("action", new RDFTermURI(action)); - bind.addBinding("name", new RDFTermLiteral(name)); - bind.addBinding("protocol", new RDFTermURI(protocol)); - bind.addBinding("dataType", new RDFTermURI(dataType)); - actionsWithInput.update(bind); - } - - public void addEvent(String event,String name){ - Bindings bind = new Bindings(); - bind.addBinding("thing", thing); - bind.addBinding("event", new RDFTermURI(event)); - bind.addBinding("name", new RDFTermLiteral(name)); - events.update(bind); - } - - public void addEvent(String event,String name,String dataType){ - Bindings bind = new Bindings(); - bind.addBinding("thing", thing); - bind.addBinding("event", new RDFTermURI(event)); - bind.addBinding("name", new RDFTermLiteral(name)); - bind.addBinding("dataType", new RDFTermURI(dataType)); - eventsWithOutput.update(bind); - } - - public void addPropertyChangedEvent(String event,String name,String dataType){ - Bindings bind = new Bindings(); - bind.addBinding("thing", thing); - bind.addBinding("event", new RDFTermURI(event)); - bind.addBinding("name", new RDFTermLiteral(name)); - bind.addBinding("dataType", new RDFTermURI(dataType)); - propertyChangeEvents.update(bind); - } - - public void addTargetProperty(String action_OR_event,String property){ - Bindings bind = new Bindings(); - bind.addBinding("action_OR_event", new RDFTermURI(action_OR_event)); - bind.addBinding("property", new RDFTermURI(property)); - targetProperties.update(bind); - } - - public void addProtocol(String action,String protocol){ - Bindings bind = new Bindings(); - bind.addBinding("action", new RDFTermURI(action)); - bind.addBinding("protocol", new RDFTermURI(protocol)); - protocols.update(bind); - } - - class ThingDescriptionPublisher extends Producer { - - public ThingDescriptionPublisher() throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_INIT"); - } - } - - class PropertyPublisher extends Producer { - - public PropertyPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_ADD_PROPERTY"); - } - } - - class ActionPublisher extends Producer { - - public ActionPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_ADD_ACTION"); - } - } - - class ActionWithInputPublisher extends Producer { - - public ActionWithInputPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_ADD_ACTION_WITH_INPUT"); - } - } - - class EventPublisher extends Producer { - - public EventPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_ADD_EVENT"); - } - } - - class EventWithOutputPublisher extends Producer { - - public EventWithOutputPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_ADD_EVENT_WITH_OUTPUT"); - } - } - - class PropertyChangeEventPublisher extends Producer { - - public PropertyChangeEventPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_ADD_PROPERTY_CHANGED_EVENT"); - } - } - - class TargetPropertyPublisher extends Producer { - - public TargetPropertyPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_APPEND_TARGET_PROPERTY_TO_ACTION_OR_EVENT"); - } - } - - class ProtocolPublisher extends Producer { - - public ProtocolPublisher() - throws SEPAProtocolException, SEPASecurityException { - super(app, "TD_APPEND_ACCESS_PROTOCOL_TO_ACTION"); - } - } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discoverable.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discoverable.java deleted file mode 100644 index 24a43564..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discoverable.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.unibo.arces.wot.framework.discovery; - -public abstract class Discoverable { - -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discovery.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discovery.java deleted file mode 100644 index 8a42f976..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/discovery/Discovery.java +++ /dev/null @@ -1,105 +0,0 @@ -package it.unibo.arces.wot.framework.discovery; - -import java.util.HashSet; -import java.util.Observable; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; - -public class Discovery extends Observable { - - private ApplicationProfile app; - - private GetAllThings getAllThings; - - public enum DiscoveryEventType {THINGS,EVENTS,ACTIONS,PROPERTIES}; - - public class DiscoveryEvent { - private DiscoveryEventType type; - private HashSet results; - private boolean added; - - public DiscoveryEvent(DiscoveryEventType type,HashSet results,boolean added) { - this.type = type; - this.results = results; - this.added = added; - } - - public DiscoveryEventType getType(){ - return type; - } - - public HashSet getResults(){ - return results; - } - - public boolean isAdded() { - return added; - } - - public boolean isRemoved() { - return !added; - } - } - - public Discovery() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - this(new ApplicationProfile("td.jsap")); - } - - public Discovery(ApplicationProfile app) throws SEPAProtocolException, SEPASecurityException{ - this.app = app; - - getAllThings = new GetAllThings(); - } - - public void enableAllThingsDiscovery() { - getAllThings.subscribe(null); - } - - public void disableAllThingsDiscovery() { - getAllThings.unsubscribe(); - } - - class GetAllThings extends Consumer { - - public GetAllThings() throws SEPAProtocolException, SEPASecurityException { - super(app, "ALL_THINGS"); - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onAddedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Action.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Action.java deleted file mode 100644 index bf333018..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Action.java +++ /dev/null @@ -1,38 +0,0 @@ -package it.unibo.arces.wot.framework.elements; - -/** - * From Web Things TD: - * - * { - "@type": ["Action","actuator:on", "building:DrainLiquidAction"], - "name": "pumpOn", - "links": [{ - "href" : "coap://w3cwot.sytes.net:5689/on", - "mediaType": "application/json" - }] - } - - { - "@type": ["Property", "building:OverflowStatus"], - "name": "overflowStatus", - "outputData": {"valueType": { "type": "boolean" }}, - "writable": false, - "links": [{ - "href" : "coap://w3cwot.sytes.net:5689/overflowStatus", - "mediaType": "application/json" - } - - * @author luca - * - */ -public class Action { - -// private String name; -// private String href; -// private String mediaType; - -// public Action(String thingURI,String name,String href) { -// this.name = name; -// this.href = href; -// } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Context.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Context.java deleted file mode 100644 index f8bedce2..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Context.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.unibo.arces.wot.framework.elements; - -public class Context { - -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Event.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Event.java deleted file mode 100644 index 777df018..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Event.java +++ /dev/null @@ -1,81 +0,0 @@ -package it.unibo.arces.wot.framework.elements; - -/** - * - * { - "@type": ["Event"], - "name": "change", - "outputData": { - "type":"object", - "properties": { - "operationStatus": { - "type": "boolean" - }, - "operationMode": { - "type": "string" - }, - "desiredTemp": { - "type": "number", - "minimun": 16, - "maximum": 30 - }, - "windVolumeLevel": { - "type": "number", - "minimun": 0, - "maximum": 8 - } - } - }, - "link": [{ - "href": "change", - "mediaType": "application/json" - } - - * @author luca - * - */ -public class Event { - private String event; - private String thing; - private String timestamp; - private String value; - - public Event(String event,String thing,String timestamp,String value) { - this.event = event; - this.thing = thing; - this.timestamp = timestamp; - this.value = value; - } - - public Event(String event,String thing,String timestamp) { - this(event,thing,timestamp,null); - } - - public String getEvent() { - return event; - } - - public String getThing() { - return thing; - } - - public String getTimestamp() { - return timestamp; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - return "Web Thing: "+thing+" ==> event *"+event+"* @ "+timestamp+ " with value: "+value; - } - - @Override - public boolean equals(Object e) { - if (!e.getClass().equals(Event.class)) return false; - Event e1 = (Event) e ; - return e1.getEvent().equals(this.getEvent()); - } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Property.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Property.java deleted file mode 100644 index 9bf73605..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Property.java +++ /dev/null @@ -1,33 +0,0 @@ -package it.unibo.arces.wot.framework.elements; - -/** - * - * { - "@type": ["Property", "building:OverflowStatus"], - "name": "overflowStatus", - "outputData": {"valueType": { "type": "boolean" }}, - "writable": false, - "links": [{ - "href" : "coap://w3cwot.sytes.net:5689/overflowStatus", - "mediaType": "application/json" - } - - * @author luca - * - */ -public class Property { - -// private String name; -// private String href; -// private String mediaType; -// private boolean writable; -// private String outputDataType; -// -// public void Property(String name,String href,String mediaType,boolean writable,String outputDataType) { -// this.name = name; -// this.href = href; -// this.mediaType = mediaType; -// this.writable = writable; -// this.outputDataType = outputDataType; -// } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Thing.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Thing.java deleted file mode 100644 index 0554a10e..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/elements/Thing.java +++ /dev/null @@ -1,28 +0,0 @@ -package it.unibo.arces.wot.framework.elements; - -import java.util.HashSet; - -import it.unibo.arces.wot.framework.discovery.Discoverable; - -public class Thing extends Discoverable { - - private String uri; - private Context context; - private HashSet properties; - - public Thing(String uri) { - this.uri = uri; - } - - public boolean equals(Thing eq) {return this.getURI().equals(eq.getURI());} - - public String getURI() {return uri;} - - public HashSet getProperties() {return properties;} - - public Context getContext() {return context;} - - public void setContext(Context context) {this.context=context;} - - public void addProperties(Property property) {properties.add(property);} -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionListener.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionListener.java deleted file mode 100644 index d369f6ff..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionListener.java +++ /dev/null @@ -1,85 +0,0 @@ -package it.unibo.arces.wot.framework.interaction; - -import java.util.HashMap; - -import it.unibo.arces.wot.framework.elements.Action; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; - -public abstract class ActionListener { - private ApplicationProfile app; - - private HashMap actionListener = new HashMap(); - - public abstract void onAction(Action action); - public abstract void onConnectionStatus(Boolean on); - public abstract void onConnectionError(ErrorResponse error); - - private class WoTActionListener extends Consumer { - - public WoTActionListener() throws SEPAProtocolException, SEPASecurityException { - super(app, "ACTION"); - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onAddedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - } - - public void startListeningForAction(String actionURI) throws SEPAProtocolException, SEPASecurityException { - if (actionListener.containsKey(actionURI)) return; - WoTActionListener listener = new WoTActionListener(); - Bindings bindings = new Bindings(); - bindings.addBinding("action", new RDFTermURI(actionURI)); - listener.subscribe(bindings); - actionListener.put(actionURI, listener); - } - - public void stopListeningForAction(String actionURI) { - if (!actionListener.containsKey(actionURI)) return; - actionListener.get(actionURI).unsubscribe(); - actionListener.remove(actionURI); - } - - public ActionListener() throws SEPAPropertiesException { - app = new ApplicationProfile("td.jsap"); - } - - public ActionListener(ApplicationProfile app) { - this.app = app; - } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionPublisher.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionPublisher.java deleted file mode 100644 index 59b2acb7..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/ActionPublisher.java +++ /dev/null @@ -1,57 +0,0 @@ -package it.unibo.arces.wot.framework.interaction; - -import java.util.UUID; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class ActionPublisher extends Producer { - private ApplicationProfile app; - - private String action; - private ActionPubliserWithInput publisherWithInput; - - class ActionPubliserWithInput extends Producer { - - public ActionPubliserWithInput() throws SEPAProtocolException, SEPASecurityException { - super(app, "POST_ACTION_WITH_INPUT"); - } - - } - - public ActionPublisher(String action) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("td.jsap"), "POST_ACTION"); - - this.app = new ApplicationProfile("td.jsap"); - this.action = action; - this.publisherWithInput = new ActionPubliserWithInput(); - } - - public ActionPublisher(ApplicationProfile app, String action) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("td.jsap"), "POST_ACTION"); - - this.app = app; - this.action = action; - this.publisherWithInput = new ActionPubliserWithInput(); - } - - public void post() { - Bindings bind = new Bindings(); - bind.addBinding("action", new RDFTermURI(action)); - bind.addBinding("newInstance", new RDFTermURI("wot:" + UUID.randomUUID())); - update(bind); - } - - public void post(String value, String dataTypeURI) { - Bindings bind = new Bindings(); - bind.addBinding("action", new RDFTermURI(action)); - bind.addBinding("value", new RDFTermLiteral(value)); - publisherWithInput.update(bind); - } -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventListener.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventListener.java deleted file mode 100644 index cb65080d..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventListener.java +++ /dev/null @@ -1,170 +0,0 @@ -package it.unibo.arces.wot.framework.interaction; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -import it.unibo.arces.wot.framework.elements.Event; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; - -public abstract class EventListener { - private ApplicationProfile app; - private HashMap> thingEventListener = new HashMap>(); - private HashMap allEventListener = new HashMap(); - - public abstract void onEvent(Set events); - public abstract void onConnectionStatus(Boolean on); - public abstract void onConnectionError(ErrorResponse error); - - public void startListeningForEvent(String eventURI) throws SEPAProtocolException, SEPASecurityException { - if (allEventListener.containsKey(eventURI)) return; - AllEventListener listener = new AllEventListener(eventURI); - allEventListener.put(eventURI, listener); - Bindings bindings = new Bindings(); - bindings.addBinding("event", new RDFTermURI(eventURI)); - allEventListener.get(eventURI).subscribe(bindings); - } - - public void stopListeningForEvent(String eventURI) { - if (!allEventListener.containsKey(eventURI)) return; - allEventListener.get(eventURI).unsubscribe(); - allEventListener.remove(eventURI); - } - - public void startListeningForEvent(String eventURI,String thingURI) throws SEPAProtocolException, SEPASecurityException { - if (thingEventListener.containsKey(thingURI)) { - HashMap thingEvents = thingEventListener.get(thingURI); - if (thingEvents.containsKey(eventURI)) return; - ThingEventListener listener = new ThingEventListener(thingURI,eventURI); - thingEvents.put(eventURI, listener); - Bindings bindings = new Bindings(); - bindings.addBinding("event", new RDFTermURI(eventURI)); - bindings.addBinding("thing", new RDFTermURI(thingURI)); - thingEvents.get(eventURI).subscribe(bindings); - } - else { - HashMap thingEvents = new HashMap(); - ThingEventListener listener = new ThingEventListener(thingURI,eventURI); - thingEvents.put(eventURI, listener); - Bindings bindings = new Bindings(); - bindings.addBinding("event", new RDFTermURI(eventURI)); - bindings.addBinding("thing", new RDFTermURI(thingURI)); - thingEventListener.put(thingURI, thingEvents); - thingEvents.get(eventURI).subscribe(bindings); - } - } - - public void stopListeningForEvent(String eventURI,String thingURI) { - if (!thingEventListener.containsKey(thingURI)) return; - if (!thingEventListener.get(thingURI).containsKey(eventURI)) return; - thingEventListener.get(thingURI).get(eventURI).unsubscribe(); - thingEventListener.get(thingURI).remove(eventURI); - } - - class AllEventListener extends Consumer { - private String event; - - public AllEventListener(String event) throws SEPAProtocolException, SEPASecurityException { - super(app, "EVENT"); - - this.event = event; - } - - //Variables: ?thing ?timeStamp OPTIONAL : ?value - @Override - public void onAddedResults(BindingsResults results) { - HashSet ret = new HashSet(); - for(Bindings bindings : results.getBindings()) { - bindings.getBindingValue("thing"); - ret.add(new Event(event,bindings.getBindingValue("thing"),bindings.getBindingValue("timeStamp"),bindings.getBindingValue("value"))); - } - onEvent(ret); - } - - @Override - public void onRemovedResults(BindingsResults results) { - - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - onConnectionStatus(false); - - } - - @Override - public void onError(ErrorResponse errorResponse) { - onConnectionError(errorResponse); - } - } - - class ThingEventListener extends Consumer { - private String thing; - private String event; - - public ThingEventListener(String thing,String event) throws SEPAProtocolException, SEPASecurityException { - super(app, "THING_EVENT"); - - this.thing = thing; - this.event = event; - } - - @Override - public void onResults(ARBindingsResults results) { - - } - - //Variables: ?timeStamp OPTIONAL : ?value - @Override - public void onAddedResults(BindingsResults results) { - HashSet ret = new HashSet(); - for(Bindings bindings : results.getBindings()) { - bindings.getBindingValue("thing"); - ret.add(new Event(thing,event,bindings.getBindingValue("timeStamp"),bindings.getBindingValue("value"))); - } - onEvent(ret); - } - - @Override - public void onRemovedResults(BindingsResults results) { - - } - - @Override - public void onBrokenSocket() { - onConnectionStatus(false); - - } - - @Override - public void onError(ErrorResponse errorResponse) { - onConnectionError(errorResponse); - - } - - } - - public EventListener() throws SEPAPropertiesException { - app = new ApplicationProfile("td.jsap"); - } - - public EventListener(ApplicationProfile app) { - this.app = app; - } - -} diff --git a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventPublisher.java b/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventPublisher.java deleted file mode 100644 index a482a1da..00000000 --- a/client-wot-framework/src/main/java/it/unibo/arces/wot/framework/interaction/EventPublisher.java +++ /dev/null @@ -1,64 +0,0 @@ -package it.unibo.arces.wot.framework.interaction; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class EventPublisher { - private ApplicationProfile app; - - private String thing; - private EventPubliserWithOutput publisherWithOutput; - private EventPubliserWithoutOutput publisherWithoutOutput; - - public EventPublisher(String thingURI) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - - this.app = new ApplicationProfile("td.jsap"); - this.thing = thingURI; - this.publisherWithOutput = new EventPubliserWithOutput(); - this.publisherWithoutOutput = new EventPubliserWithoutOutput(); - } - - public EventPublisher(ApplicationProfile app,String thingURI) throws SEPAProtocolException, SEPASecurityException { - - this.app = app; - this.thing = thingURI; - this.publisherWithOutput = new EventPubliserWithOutput(); - this.publisherWithoutOutput = new EventPubliserWithoutOutput(); - } - - class EventPubliserWithOutput extends Producer { - - public EventPubliserWithOutput() throws SEPAProtocolException, SEPASecurityException { - super(app, "POST_EVENT_WITH_OUTPUT"); - } - } - - class EventPubliserWithoutOutput extends Producer { - - public EventPubliserWithoutOutput() throws SEPAProtocolException, SEPASecurityException { - super(app, "POST_EVENT"); - } - } - - public void post(String eventURI) { - Bindings bind = new Bindings(); - bind.addBinding("event", new RDFTermURI(eventURI)); - bind.addBinding("thing", new RDFTermURI(thing)); - publisherWithoutOutput.update(bind); - } - - public void post(String eventURI,String value) { - Bindings bind = new Bindings(); - bind.addBinding("thing", new RDFTermURI(thing)); - bind.addBinding("event", new RDFTermURI(eventURI)); - bind.addBinding("value", new RDFTermLiteral(value)); - publisherWithOutput.update(bind); - } - -} diff --git a/client-wot-framework/src/test/java/it/unibo/arces/wot/wot_api/AppTest.java b/client-wot-framework/src/test/java/it/unibo/arces/wot/wot_api/AppTest.java deleted file mode 100644 index 61578625..00000000 --- a/client-wot-framework/src/test/java/it/unibo/arces/wot/wot_api/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package it.unibo.arces.wot.wot_api; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} diff --git a/commons/LICENSE b/commons/LICENSE deleted file mode 100644 index 9cecc1d4..00000000 --- a/commons/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/commons/pom.xml b/commons/pom.xml deleted file mode 100644 index 6e2e19cf..00000000 --- a/commons/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - 4.0.0 - - it.unibo.arces.wot - sepa - ${revision} - - commons - - - - org.apache.logging.log4j - log4j-api - 2.8.1 - - - - org.apache.logging.log4j - log4j-core - 2.8.1 - - - - com.google.code.gson - gson - 2.8.0 - - - - org.apache.httpcomponents - httpclient - 4.5.2 - - - - org.apache.httpcomponents - httpcore - 4.4.6 - - - - - - - - - - - - diff --git a/commons/src/main/resources/log4j2.xml b/commons/src/main/resources/log4j2.xml deleted file mode 100644 index f0b0d77e..00000000 --- a/commons/src/main/resources/log4j2.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/engine/endpoints/endpoint-stardog.jpar b/engine/endpoints/endpoint-stardog.jpar new file mode 100644 index 00000000..651bc5bc --- /dev/null +++ b/engine/endpoints/endpoint-stardog.jpar @@ -0,0 +1,29 @@ +{ + "host": "localhost", + "sparql11protocol": { + "protocol": "http", + "port": 5280, + "query": { + "path": "/dotone/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/dotone/update", + "method": "POST", + "format": "JSON" + }, + "authentication": { + "basic": { + "user": "admin", + "pass": "admin" + } + } + }, + "graphs": { + "default-graph-uri": "http://www.dotone.eu/", + "named-graph-uri": "http://www.dotone.eu/", + "using-graph-uri": "http://www.dotone.eu/", + "using-named-graph-uri": "http://www.dotone.eu/" + } +} \ No newline at end of file diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java index 9ead148c..7c1683cb 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java @@ -26,9 +26,9 @@ public class ProcessorBeans { private static int queryTimeout; public static void setEndpoint(SPARQL11Properties prop) { - host = prop.getHost(); - port = prop.getHttpPort(); - queryPath = prop.getQueryPath(); + host = prop.getDefaultHost(); + port = prop.getDefaultPort(); + queryPath = prop.getDefaultQueryPath(); updatePath = prop.getUpdatePath(); updateMethod = prop.getUpdateMethod().name(); queryMethod = prop.getQueryMethod().name(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 54cb1963..695fc32e 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -242,7 +242,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException processor.start(); // SPARQL protocol service - int port = endpointProperties.getHttpPort(); + int port = endpointProperties.getDefaultPort(); String portS = ""; if (port != -1) portS = String.format(":%d", port); @@ -275,9 +275,9 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException System.out.println("SPARQL 1.1 endpoint"); System.out.println("----------------------"); - System.out.println("SPARQL 1.1 Query | http://" + endpointProperties.getHost() + portS - + endpointProperties.getQueryPath() + queryMethod); - System.out.println("SPARQL 1.1 Update | http://" + endpointProperties.getHost() + portS + System.out.println("SPARQL 1.1 Query | http://" + endpointProperties.getDefaultHost() + portS + + endpointProperties.getDefaultQueryPath() + queryMethod); + System.out.println("SPARQL 1.1 Update | http://" + endpointProperties.getDefaultHost() + portS + endpointProperties.getUpdatePath() + updateMethod); System.out.println("----------------------"); System.out.println(""); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index b57ce077..c0305d3f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -52,7 +52,6 @@ public class Processor extends Thread implements ProcessorMBean { // Scheduler queue private SchedulerRequestResponseQueue queue; - // Concurrent endpoint limit private Semaphore endpointSemaphore = null; @@ -77,7 +76,6 @@ public Processor(SPARQL11Properties endpointProperties, EngineProperties propert // SPU manager subscribeProcessor = new SubscribeProcessor(endpointProperties, properties, endpointSemaphore); - // subscribeProcessor.addObserver(this); // JMX SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); @@ -103,7 +101,8 @@ public void run() { logger.debug(request); // Process update request - Response ret = updateProcessor.process((UpdateRequest) request, ProcessorBeans.getUpdateTimeout()); + request.setTimeout(ProcessorBeans.getUpdateTimeout()); + Response ret = updateProcessor.process((UpdateRequest) request); // // Notify update result queue.addResponse(ret); @@ -115,9 +114,11 @@ public void run() { logger.info("Query request #" + request.getToken()); logger.debug(request); + request.setTimeout(ProcessorBeans.getQueryTimeout()); + Thread queryProcessing = new Thread() { public void run() { - Response ret = queryProcessor.process((QueryRequest) request, ProcessorBeans.getQueryTimeout()); + Response ret = queryProcessor.process((QueryRequest) request); queue.addResponse(ret); } }; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index 17cc5eb6..7e8d17b0 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -18,8 +18,10 @@ package it.unibo.arces.wot.sepa.engine.processing; +import java.io.UnsupportedEncodingException; import java.util.concurrent.Semaphore; +import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -29,42 +31,54 @@ import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; + import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; import it.unibo.arces.wot.sepa.timing.Timings; public class QueryProcessor { private static final Logger logger = LogManager.getLogger(); - private SPARQL11Protocol endpoint; + private SPARQL11Protocol endpoint; private Semaphore endpointSemaphore; private SPARQL11Properties properties; - - public QueryProcessor(SPARQL11Properties properties,Semaphore endpointSemaphore) throws SEPAProtocolException { - this.endpoint = new SPARQL11Protocol(properties); + + public QueryProcessor(SPARQL11Properties properties, Semaphore endpointSemaphore) throws SEPAProtocolException { + this.endpoint = new SPARQL11Protocol(); this.endpointSemaphore = endpointSemaphore; this.properties = properties; } - public synchronized Response process(QueryRequest req, int timeout) { + public synchronized Response process(QueryRequest req) { long start = Timings.getTime(); - + if (endpointSemaphore != null) try { endpointSemaphore.acquire(); } catch (InterruptedException e) { - return new ErrorResponse(500,e.getMessage()); + return new ErrorResponse(500, e.getMessage()); } - - //QUERY the endpoint - Response ret = endpoint.query(req, timeout,properties.getQueryMethod()); - - if (endpointSemaphore != null) endpointSemaphore.release(); + + Response ret; + QueryRequest request; + try { + request = new QueryRequest(req.getToken(), properties.getQueryMethod(), properties.getDefaultProtocolScheme(), + properties.getDefaultHost(), properties.getDefaultPort(), properties.getDefaultQueryPath(), + req.getSPARQL(), req.getTimeout(), req.getDefaultGraphUri(), req.getNamedGraphUri()); + } catch (UnsupportedEncodingException e) { + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + try { + ret = endpoint.query(request); + } finally { + if (endpointSemaphore != null) + endpointSemaphore.release(); + } long stop = Timings.getTime(); - logger.debug("Response: "+ret.toString()); + logger.debug("Response: " + ret.toString()); Timings.log("QUERY_PROCESSING_TIME", start, stop); ProcessorBeans.queryTimings(start, stop); - + return ret; } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index 9ae05c9b..b0191be9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -18,8 +18,10 @@ package it.unibo.arces.wot.sepa.engine.processing; +import java.io.UnsupportedEncodingException; import java.util.concurrent.Semaphore; +import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -38,33 +40,42 @@ public class UpdateProcessor { private SPARQL11Protocol endpoint; private Semaphore endpointSemaphore; private SPARQL11Properties properties; - - public UpdateProcessor(SPARQL11Properties properties,Semaphore endpointSemaphore) throws SEPAProtocolException { - endpoint = new SPARQL11Protocol(properties); + + public UpdateProcessor(SPARQL11Properties properties, Semaphore endpointSemaphore) throws SEPAProtocolException { + endpoint = new SPARQL11Protocol(); this.endpointSemaphore = endpointSemaphore; this.properties = properties; } - public synchronized Response process(UpdateRequest req, int timeout) { + public synchronized Response process(UpdateRequest req) { long start = Timings.getTime(); - + if (endpointSemaphore != null) try { endpointSemaphore.acquire(); } catch (InterruptedException e) { - return new ErrorResponse(500,e.getMessage()); + return new ErrorResponse(500, e.getMessage()); } - + // UPDATE the endpoint - Response ret = endpoint.update(req, timeout,properties.getUpdateMethod()); - - if (endpointSemaphore != null) endpointSemaphore.release(); + Response ret; + try { + ret = endpoint.update(new UpdateRequest(req.getToken(), properties.getUpdateMethod(), + properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), + properties.getUpdatePath(), req.getSPARQL(), req.getTimeout(), req.getUsingGraphUri(), + req.getUsingNamedGraphUri())); + } catch (UnsupportedEncodingException e) { + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } finally { + if (endpointSemaphore != null) + endpointSemaphore.release(); + } long stop = Timings.getTime(); - logger.debug("Response: "+ret.toString()); + logger.debug("Response: " + ret.toString()); Timings.log("UPDATE_PROCESSING_TIME", start, stop); ProcessorBeans.updateTimings(start, stop); - + return ret; } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index 5a8a002f..dc09bd33 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -24,16 +24,15 @@ import java.util.concurrent.Semaphore; import it.unibo.arces.wot.sepa.engine.processing.QueryProcessor; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; - import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.Notification; -//import it.unibo.arces.wot.sepa.commons.response.Ping; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index 5200e428..2a169c6b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -32,7 +32,6 @@ import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; import java.util.concurrent.Semaphore; @@ -58,7 +57,7 @@ public Response init() { logger.debug("Process SPARQL query " + request); // Process the SPARQL query - Response ret = queryProcessor.process(request, ProcessorBeans.getQueryTimeout()); + Response ret = queryProcessor.process(request); if (ret.getClass().equals(ErrorResponse.class)) { logger.error("Not initialized"); @@ -74,13 +73,14 @@ public Response init() { } @Override - public Response processInternal(UpdateResponse update, int timeout) { + public Response processInternal(UpdateResponse update,int timeout) { logger.debug("* PROCESSING *" + request); Response ret; try { // Query the SPARQL processing service - ret = queryProcessor.process(request, timeout); + request.setTimeout(timeout); + ret = queryProcessor.process(request); if (ret.getClass().equals(ErrorResponse.class)) { logger.error(ret); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java index 8acc0aa8..7f48efea 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java @@ -1,9 +1,9 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.Charset; +import java.util.Map; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -18,6 +18,7 @@ import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.request.Request; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; /** @@ -40,7 +41,6 @@ *---------------------------------------------------------------------------------------------------------------------------------------- * query via POST directly | POST default-graph-uri (0 or more) * | named-graph-uri (0 or more) application/sparql-query Unencoded SPARQL query string - using-named-graph-uri (0 or more) * * 2.1.4 Specifying an RDF Dataset * @@ -61,85 +61,83 @@ * @see UpdateRequest */ public class QueryHandler extends SPARQL11Handler { - protected static final Logger logger = LogManager.getLogger("QueryHandler"); - + protected static final Logger logger = LogManager.getLogger(); + public QueryHandler(Scheduler scheduler) throws IllegalArgumentException { super(scheduler); } @Override - protected Request parse(HttpAsyncExchange exchange) { + protected Request parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolException { switch (exchange.getRequest().getRequestLine().getMethod().toUpperCase()) { case "GET": logger.debug("query via GET"); - String requestUri = exchange.getRequest().getRequestLine().getUri(); - if (requestUri.indexOf('?') == -1) { - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST,"Wrong request uri: ? not found in "+requestUri); - } - - String queryParameters = requestUri.substring(requestUri.indexOf('?') + 1); - - if (!queryParameters.contains("query=")) { - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, "Wrong request uri: 'query=' not found in "+queryParameters); - } - String[] query = queryParameters.split("&"); - for (String param : query) { - String[] value = param.split("="); - if (value[0].equals("query")) { - String sparql = ""; - try { - sparql = URLDecoder.decode(value[1], "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage()); - } - - return new QueryRequest(sparql); + try { + String requestUri = exchange.getRequest().getRequestLine().getUri(); + + if (requestUri.indexOf('?') == -1) { + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, + "Wrong request uri: ? not found in " + requestUri); } - } - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, "Wrong format: " + exchange.getRequest().getRequestLine()); - case "POST": - String body = null; - HttpEntity entity = ((HttpEntityEnclosingRequest) exchange.getRequest()).getEntity(); - try { - body = EntityUtils.toString(entity, Charset.forName("UTF-8")); - } catch (ParseException | IOException e) { - body = e.getLocalizedMessage(); - } + String queryParameters = requestUri.substring(requestUri.indexOf('?') + 1); + Map params = HttpUtilities.splitQuery(queryParameters); - Header[] headers = exchange.getRequest().getHeaders("Content-Type"); - if (headers.length != 1) { - logger.error("Content-Type is missing"); - throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); + if (params.get("query") == null) { + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, + "Wrong request uri: 'query=' not found in " + queryParameters); + } + + String sparql = URLDecoder.decode(params.get("query"), "UTF-8"); + String graphUri = params.get("default-graph-uri"); + String namedGraphUri = params.get("named-graph-uri"); + + return new QueryRequest(sparql, graphUri, namedGraphUri); + } catch (Exception e) { + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage()); } + case "POST": + try { + HttpEntity entity = ((HttpEntityEnclosingRequest) exchange.getRequest()).getEntity(); + String body = EntityUtils.toString(entity, Charset.forName("UTF-8")); - if (headers[0].getValue().equals("application/sparql-query")) { - logger.debug("query via POST directly"); - - return new QueryRequest(body); - } else if (headers[0].getValue().equals("application/x-www-form-urlencoded")) { - String decodedBody; - try { - decodedBody = URLDecoder.decode(body, "UTF-8"); - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, e.getMessage()); + Header[] headers = exchange.getRequest().getHeaders("Content-Type"); + if (headers.length != 1) { + logger.error("Content-Type is missing"); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); } - String[] parameters = decodedBody.split("&"); - for (String param : parameters) { - String[] value = param.split("="); - if (value[0].equals("query")) { - logger.debug("query via URL-encoded"); - - return new QueryRequest(value[1]); + if (headers[0].getValue().equals("application/sparql-query")) { + logger.debug("query via POST directly"); + + String requestUri = exchange.getRequest().getRequestLine().getUri(); + String graphUri = null; + String namedGraphUri = null; + + if (requestUri.indexOf('?') != -1) { + String queryParameters = requestUri.substring(requestUri.indexOf('?') + 1); + Map params = HttpUtilities.splitQuery(queryParameters); + graphUri = params.get("default-graph-uri"); + namedGraphUri = params.get("named-graph-uri"); } + + return new QueryRequest(body, graphUri, namedGraphUri); + } else if (headers[0].getValue().equals("application/x-www-form-urlencoded")) { + String decodedBody = URLDecoder.decode(body, "UTF-8"); + Map params = HttpUtilities.splitQuery(decodedBody); + return new QueryRequest(params.get("query"), params.get("default-graph-uri"), + params.get("named-graph-uri")); } + + logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, + "Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + + } catch (ParseException | IOException e) { + logger.error(e.getMessage()); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage()); } - logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); - throw new SPARQL11ProtocolException(HttpStatus.SC_NOT_FOUND, - "Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); } logger.error("UNSUPPORTED METHOD: " + exchange.getRequest().getRequestLine().getMethod().toUpperCase()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java index 28697efe..2fb2dc92 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.Map; @@ -77,29 +78,33 @@ protected Request parse(HttpAsyncExchange exchange) { if (contentType.equals("application/sparql-update")) { logger.debug("update via POST directly"); - UpdateRequest ret = new UpdateRequest(body); + String usingGraphUri = null; + String usingNamedGraphUri = null; + try { String requestUri = exchange.getRequest().getRequestLine().getUri(); if (requestUri.indexOf('?') != -1) { String[] split = requestUri.split("\\?"); if (split.length == 2) { Map params = HttpUtilities.splitQuery(split[1]); - if (params.get("using-graph-uri") != null) ret.setUsingGraphUri(params.get("using-graph-uri")); - if (params.get("named-graph-uri") != null) ret.setNamedGraphUri(params.get("named-graph-uri")); + if (params.get("using-graph-uri") != null) usingGraphUri = URLDecoder.decode(params.get("using-graph-uri"), "UTF-8"); + if (params.get("using-named-graph-uri") != null) usingNamedGraphUri = URLDecoder.decode(params.get("using-named-graph-uri"), "UTF-8"); } } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, e.getMessage()); - } + } - return ret; + return new UpdateRequest(body,usingGraphUri,usingNamedGraphUri); } else if (contentType.equals("application/x-www-form-urlencoded")) { try { - Map params = HttpUtilities.splitQuery(body); + String decodedBody = URLDecoder.decode(body, "UTF-8"); + Map params = HttpUtilities.splitQuery(decodedBody); logger.debug("update via URL ENCODED POST directly: "+params.get("update")); - if (params.get("update") != null) return new UpdateRequest(params.get("update")); + if (params.get("update") != null) return new UpdateRequest(params.get("update"),params.get("using-graph-uri"),params.get("using-named-graph-uri")); + } catch (UnsupportedEncodingException e1) { logger.error(e1.getMessage()); throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, e1.getMessage()); diff --git a/engine/src/test/java/engine/QueryProcessorTest.java b/engine/src/test/java/engine/QueryProcessorTest.java index c101f7a8..b9afd970 100644 --- a/engine/src/test/java/engine/QueryProcessorTest.java +++ b/engine/src/test/java/engine/QueryProcessorTest.java @@ -23,7 +23,7 @@ public static void main(String[] args) throws SEPAProtocolException, SEPAPropert // TODO Auto-generated catch block e.printStackTrace(); } - System.out.println(processor.process(new QueryRequest("select ?s ?p ?o where {?s ?p ?o}"),0)); + System.out.println(processor.process(new QueryRequest("select ?s ?p ?o where {?s ?p ?o}"))); } } diff --git a/engine/src/test/java/engine/UpdateProcessorTest.java b/engine/src/test/java/engine/UpdateProcessorTest.java index 19dbcf50..dcbed536 100644 --- a/engine/src/test/java/engine/UpdateProcessorTest.java +++ b/engine/src/test/java/engine/UpdateProcessorTest.java @@ -23,7 +23,7 @@ public static void main(String[] args) throws SEPAPropertiesException, SEPAProto // TODO Auto-generated catch block e.printStackTrace(); } - System.out.println(processor.process(new UpdateRequest("PREFIX test: delete {?s ?p ?o} insert {test:s test:p \""+Math.random()+"\"} where {?s ?p ?o}"),0)); + System.out.println(processor.process(new UpdateRequest("PREFIX test: delete {?s ?p ?o} insert {test:s test:p \""+Math.random()+"\"} where {?s ?p ?o}"))); } } diff --git a/pom.xml b/pom.xml index 6414bcb6..14de0a4c 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,9 @@ pom The project aims at developing a SPARQL Event Processing Architecture using JAVA technologies, including APIs and tools - commons client-api - client-pac-pattern tools engine - - SEPA Project https://github.com/arces-wot/sepa.git diff --git a/tools/GatewayProfile.jsap b/tools/GatewayProfile.jsap deleted file mode 100644 index ff657a0e..00000000 --- a/tools/GatewayProfile.jsap +++ /dev/null @@ -1,513 +0,0 @@ -{ - "parameters": { - "host": "wot.arces.unibo.it", - "path": "/sparql", - "scheme": "http", - "port": 8000, - "securequery": { - "port": 8443, - "scheme": "https" - }, - "secureupdate": { - "port": 8443, - "scheme": "https" - }, - "subscribe": { - "scheme": "ws", - "port": 9000 - }, - "securesubscribe": { - "scheme": "wss", - "port": 9443, - "path": "/secure/sparql" - }, - "authorizationserver": { - "port": 8443, - "scheme": "https", - "register": "/oauth/register", - "tokenrequest": "/oauth/token" - } - }, - "namespaces": { - "iot": "http://wot.arces.unibo.it/IoTGateway#", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#" - }, - "updates": { - "DELETE_ALL": { - "sparql": "DELETE { ?s ?p ?o } WHERE { ?s ?p ?o }" - }, - "DELETE_MP_MAPPING": { - "sparql": "DELETE { ?mapping ?p ?o } WHERE { ?mapping ?p ?o . ?mapping rdf:type iot:MP-Mapping }", - "forcedBindings": { - "mapping": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_MN_MAPPING": { - "sparql": "DELETE { ?mapping ?p ?o } WHERE { ?mapping ?p ?o . ?mapping rdf:type iot:MN-Mapping }", - "forcedBindings": { - "mapping": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_RESOURCE_PENDING_REQUEST": { - "sparql": "DELETE { ?request ?p ?o } WHERE { ?request ?p ?o . ?request rdf:type iot:Resource-Pending-Request }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_RESOURCE_REQUEST": { - "sparql": "DELETE { ?request ?p ?o } WHERE { ?request ?p ?o . ?request rdf:type iot:Resource-Request }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_MN_REQUEST": { - "sparql": "DELETE { ?request ?p ?o } WHERE { ?request ?p ?o . ?request rdf:type iot:MN-Request }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_RESOURCE_RESPONSE": { - "sparql": "DELETE { ?response ?p ?o } WHERE { ?reponse ?p ?o . ?response rdf:type iot:Resource-Response }", - "forcedBindings": { - "response": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_MN_RESPONSE": { - "sparql": "DELETE { ?response ?p ?o } WHERE { ?reponse ?p ?o . ?response rdf:type iot:MN-Response }", - "forcedBindings": { - "response": { - "type": "uri", - "value": "" - } - } - }, - "DELETE_REQUEST_RESPONSE": { - "sparql": "DELETE { ?request ?p ?o . ?response ?p1 ?o1} WHERE { ?request ?p ?o . ?response ?p1 ?o1 . ?request rdf:type iot:MP-Request . ?response rdf:type iot:MP-Response }", - "forcedBindings": { - "response": { - "type": "uri", - "value": "" - }, - "request": { - "type": "uri", - "value": "" - } - } - }, - "INSERT_RESOURCE": { - "sparql": "INSERT DATA {?resource rdf:type iot:Resource . ?resource iot:hasValue ?value}", - "forcedBindings": { - "resource": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_MP_MAPPING": { - "sparql": "INSERT DATA { - ?mapping rdf:type iot:MP-Mapping . - ?mapping iot:hasProtocol ?protocol . - ?mapping iot:hasResource ?resource . - ?mapping iot:hasAction ?action . - ?mapping iot:hasValue ?value . - ?mapping iot:hasMPRequestPattern ?requestPattern . - ?mapping iot:hasMPResponsePattern ?responsePattern }", - "forcedBindings": { - "mapping": { - "type": "uri", - "value": "" - }, - "protocol": { - "type": "uri", - "value": "" - }, - "resource": { - "type": "uri", - "value": "" - }, - "action": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - }, - "requestPattern": { - "type": "literal", - "value": "" - }, - "responsePattern": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_MP_REQUEST": { - "sparql": "INSERT DATA { - ?request rdf:type iot:MP-Request . - ?request iot:hasProtocol ?protocol . - ?request iot:hasMPRequestString ?value }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - }, - "protocol": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_MP_RESPONSE": { - "sparql": "INSERT DATA { - ?response iot:generatedBy ?request . - ?response rdf:type iot:MP-Response . - ?response iot:hasMPResponseString ?value }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - }, - "response": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_MN_REQUEST": { - "sparql": "INSERT DATA { - ?request rdf:type iot:MN-Request . - ?request iot:hasNetwork ?network . - ?request iot:hasMNRequestString ?value }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - }, - "network": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_MN_RESPONSE": { - "sparql": "INSERT DATA { - ?response rdf:type iot:MN-Response . - ?response iot:hasNetwork ?network . - ?response iot:hasMNResponseString ?value }", - "forcedBindings": { - "response": { - "type": "uri", - "value": "" - }, - "network": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_MN_MAPPING": { - "sparql": "INSERT DATA { - ?mapping rdf:type iot:MN-Mapping . - ?mapping iot:hasNetwork ?network . - ?mapping iot:hasResource ?resource . - ?mapping iot:hasAction ?action . - ?mapping iot:hasValue ?value . - ?mapping iot:hasMNRequestPattern ?requestPattern . - ?mapping iot:hasMNResponsePattern ?responsePattern }", - "forcedBindings": { - "mapping": { - "type": "uri", - "value": "" - }, - "network": { - "type": "uri", - "value": "" - }, - "resource": { - "type": "uri", - "value": "" - }, - "action": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - }, - "requestPattern": { - "type": "literal", - "value": "" - }, - "responsePattern": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_RESOURCE_PENDING_REQUEST": { - "sparql": "INSERT DATA { - ?request rdf:type iot:Resource-Pending-Request . - ?request iot:hasValue ?value . - ?request iot:hasAction ?action . - ?request iot:hasResource ?resource }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - }, - "action": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_RESOURCE_RESPONSE": { - "sparql": "INSERT DATA { - ?response rdf:type iot:Resource-Response . - ?response iot:hasAction ?action . - ?response iot:hasResource ?resource . - ?response iot:hasValue ?value }", - "forcedBindings": { - "response": { - "type": "uri", - "value": "" - }, - "action": { - "type": "uri", - "value": "" - }, - "resource": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "INSERT_RESOURCE_REQUEST": { - "sparql": "INSERT DATA { - ?request rdf:type iot:Resource-Request . - ?request iot:hasValue ?value . - ?request iot:hasAction ?action . - ?request iot:hasResource ?resource }", - "forcedBindings": { - "request": { - "type": "uri", - "value": "" - }, - "action": { - "type": "uri", - "value": "" - }, - "resource": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "UPDATE_RESOURCE": { - "sparql": "DELETE { ?resource iot:hasValue ?oldValue } - INSERT { ?resource rdf:type iot:Resource . ?resource iot:hasValue ?value } - WHERE { OPTIONAL {?resource iot:hasValue ?oldValue . ?resource rdf:type iot:Resource} }", - "forcedBindings": { - "resource": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - }, - "UPDATE_MN_MAPPING": { - "sparql": "DELETE { ?mapping ?pred ?obj } - INSERT { ?mapping rdf:type iot:MN-Mapping . - ?mapping iot:hasNetwork ?network . - ?mapping iot:hasResource ?resource . - ?mapping iot:hasAction ?action . - ?mapping iot:hasValue ?value . - ?mapping iot:hasMNRequestPattern ?requestPattern . - ?mapping iot:hasMNResponsePattern ?responsePattern } - WHERE { ?mapping ?pred ?obj . ?mapping rdf:type iot:MN-Mapping }", - "forcedBindings": { - "mapping": { - "type": "uri", - "value": "" - } - } - }, - "UPDATE_MP_MAPPING": { - "sparql": "DELETE { ?mapping ?pred ?obj } - INSERT { ?mapping rdf:type iot:MP-Mapping . - ?mapping iot:hasProtocol ?protocol . - ?mapping iot:hasResource ?resource . - ?mapping iot:hasAction ?action . - ?mapping iot:hasValue ?value . - ?mapping iot:hasMPRequestPattern ?requestPattern . - ?mapping iot:hasMPResponsePattern ?responsePattern } - WHERE { ?mapping ?pred ?obj . ?mapping rdf:type iot:MP-Mapping }", - "forcedBindings": { - "mapping": { - "type": "uri", - "value": "" - } - } - } - }, - "subscribes": { - "MP_REQUEST": { - "sparql": "SELECT ?request ?protocol ?value WHERE { - ?request rdf:type iot:MP-Request . - ?request iot:hasProtocol ?protocol . - ?request iot:hasMPRequestString ?value }", - "forcedBindings": { - } - }, - "MP_RESPONSE": { - "sparql": "SELECT ?response ?request ?value ?protocol WHERE { - ?response rdf:type iot:MP-Response . - ?response iot:generatedBy ?request . - ?response iot:hasMPResponseString ?value . - ?request iot:hasProtocol ?protocol }", - "forcedBindings": { - } - }, - "MP_MAPPING": { - "sparql": "SELECT ?mapping ?protocol ?requestPattern ?responsePattern ?resource ?action ?value WHERE { - ?mapping rdf:type iot:MP-Mapping . - ?mapping iot:hasProtocol ?protocol . - ?mapping iot:hasResource ?resource . - ?mapping iot:hasAction ?action . - ?mapping iot:hasValue ?value . - ?mapping iot:hasMPRequestPattern ?requestPattern . - ?mapping iot:hasMPResponsePattern ?responsePattern }", - "forcedBindings": { - } - }, - "MN_REQUEST": { - "sparql": "SELECT ?request ?value ?network WHERE { - ?request rdf:type iot:MN-Request . - ?request iot:hasNetwork ?network . - ?request iot:hasMNRequestString ?value }", - "forcedBindings": { - } - }, - "MN_RESPONSE": { - "sparql": "SELECT ?response ?network ?value WHERE { - ?response rdf:type iot:MN-Response . - ?response iot:hasNetwork ?network . - ?response iot:hasMNResponseString ?value }", - "forcedBindings": { - } - }, - "MN_MAPPING": { - "sparql": "SELECT ?mapping ?network ?requestPattern ?responsePattern ?resource ?action ?value WHERE { - ?mapping rdf:type iot:MN-Mapping . - ?mapping iot:hasNetwork ?network . - ?mapping iot:hasResource ?resource . - ?mapping iot:hasAction ?action . - ?mapping iot:hasValue ?value . - ?mapping iot:hasMNRequestPattern ?requestPattern . - ?mapping iot:hasMNResponsePattern ?responsePattern }", - "forcedBindings": { - } - }, - "RESOURCE_REQUEST": { - "sparql": "SELECT ?request ?value ?action ?resource WHERE { - ?request rdf:type iot:Resource-Request . - ?request iot:hasValue ?value . - ?request iot:hasAction ?action . - ?request iot:hasResource ?resource }", - "forcedBindings": { - } - }, - "RESOURCE_RESPONSE": { - "sparql": "SELECT ?response ?action ?resource ?value WHERE { - ?response rdf:type iot:Resource-Response . - ?response iot:hasAction ?action . - ?response iot:hasResource ?resource . - ?response iot:hasValue ?value }", - "forcedBindings": { - } - }, - "RESOURCE_PENDING_REQUEST": { - "sparql": "SELECT ?request ?resource ?action ?value WHERE { - ?request rdf:type iot:Resource-Pending-Request . - ?request iot:hasValue ?value . - ?request iot:hasAction ?action . - ?request iot:hasResource ?resource }", - "forcedBindings": { - } - }, - "RESOURCE": { - "sparql": "SELECT ?resource ?value WHERE { - ?resource rdf:type iot:Resource . - ?resource iot:hasValue ?value }", - "forcedBindings": { - } - }, - "ALL": { - "sparql": "SELECT ?s ?p ?o WHERE { ?s ?p ?o }", - "forcedBindings": { - } - }, - "COAP_RESOURCE": { - "sparql": "SELECT ?resource WHERE { ?mapping rdf:type iot:MP-Mapping . ?mapping iot:hasProtocol iot:COAP . ?mapping iot:hasResource ?resource }", - "forcedBindings": { - } - } - } -} diff --git a/tools/bugs.jsap b/tools/bugs.jsap deleted file mode 100644 index 477b8958..00000000 --- a/tools/bugs.jsap +++ /dev/null @@ -1,74 +0,0 @@ -{ - "host": "localhost", - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/subscribe" - } - }, - "security": { - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure", - "client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs", - "client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT", - "expires": "04/5tRBT5n/VJ0XQASgs/w==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - } - }, - "extended": { - - }, - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - }, - "namespaces": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "td": "http://wot.arces.unibo.it/ontology/web_of_things#", - "qmul": "http://eecs.qmul.ac.uk/wot#", - "wot": "http://wot.arces.unibo.it/sepa#" - }, - "updates": { - "ERASE_STORE": { - "sparql": "delete {?s ?p ?o} where {?s ?p ?o}" - }, - "UPD1": { - "sparql": "INSERT DATA {qmul:bad31340-4058-4a82-8f2e-3360b88cf910 rdf:type td:Thing. qmul:bad31340-4058-4a82-8f2e-3360b88cf910 td:hasName 'fooThing'}" - }, - "UPD2": { - "sparql": "INSERT DATA {qmul:bad31340-4058-4a82-8f2e-3360b88cf910 td:hasEvent qmul:00f95a14-b605-41cb-85a6-543e57be9c45 . qmul:00f95a14-b605-41cb-85a6-543e57be9c45 rdf:type td:Event . qmul:00f95a14-b605-41cb-85a6-543e57be9c45 td:hasName 'fooEvent' . qmul:00f95a14-b605-41cb-85a6-543e57be9c45 wot:hasOutputDataSchema '-'}" - } - }, - "queries": { - "QUERY_ALL": { - "sparql": "select * where {?s ?p ?o}" - }, - "SUB1": { - "sparql": "SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName }}" - } - } -} diff --git a/tools/chat.jsap b/tools/chat.jsap deleted file mode 100644 index 19e4b34e..00000000 --- a/tools/chat.jsap +++ /dev/null @@ -1,175 +0,0 @@ -{ - "host": "localhost", - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/subscribe" - } - }, - "security": { - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure", - "client_id": "jaJBrmgtqgW9jTLHeVbzSCH6ZIN1Qaf3XthmwLxjhw3WuXtt7VELmfibRNvOdKLs", - "client_secret": "fkITPTMsHUEb9gVVRMP5CAeIE1LrfBYtNLdqtlTVZ/CqgqcuzEw+ZcVegW5dMnIg", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvx34vSSnhpkfcdYbZ+7KDaK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkfP7DEKe7LScGYaT4RcuIfNmywI4fAWabAI4zqedYbd5lXmYhbSmXviPTOQPKxhmZptZ6F5Q178nfK6Bik4/0PwUlgMsC6oVFeJtyPWvjfEP0nx9tGMOt+z9Rvbd7enGWRFspUQJS2zzmGlHW1m5QNFdtOCfTLUOKkyZV4JUQxI1CaP+QbIyIihuQDvIMbmNgbvDNBkj9VQOzg1WB7mj4nn4w7T8I9MpOxAXxnaPUvDk8QnL/5leQcUiFVTa1zlzambQ8xr/BojFB52fIz8LsrDRW/+/0CJJVTFYD6OZ/gepFyLK4yOu/rOiTLT5CF9H2NZQd7bi85zSmi50RHFa3358LvL50c4G84Gz7mkDTBV9JxBhlWVNvD5VR58rPcgESwlGEL2YmOQCZzYGWjTc5cyI/50ZX83sTlTbfs+Tab3pBlsRQu36iNznleeKPj6uVvql+3uvcjMEBqqXvj8TKxMi9tCfHA1vt9RijOap8ROHtnIe4iMovPzkOCMiHJPcwbnyi+6jHbrPI18WGghceZQT23qKHDUYQo2NiehLQG9MQZA1Ncx2w4evBTBX8lkBS4aLoCUoTZTlNFSDOohUHJCbeig9eV77JbLo0a4+PNH9bgM/icSnIG5TidBGyJpEkVtD7+/KphwM89izJam3OT", - "expires": "04/5tRBT5n/VJ0XQASgs/w==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - } - }, - "extended": { - "type": "basic", - "base": 0, - "clients": 10, - "messages": 1 - }, - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - }, - "namespaces": { - "schema": "http://schema.org/", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - }, - "updates": { - "SEND": { - "sparql": "INSERT {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?time} WHERE {?sender rdf:type schema:Person . ?receiver rdf:type schema:Person BIND(STR(now()) AS ?time) BIND(IRI(CONCAT(\"http://schema.org/Message-\",STRUUID())) AS ?message)}", - "forcedBindings": { - "text": { - "type": "literal", - "value": "" - }, - "sender": { - "type": "uri", - "value": "" - }, - "receiver": { - "type": "uri", - "value": "" - } - } - }, - "SET_RECEIVED": { - "sparql": "INSERT {?message schema:dateReceived ?time} WHERE {?message rdf:type schema:Message BIND(STR(now()) AS ?time)}", - "forcedBindings": { - "message": { - "type": "uri", - "value": "" - } - } - }, - "REMOVE": { - "sparql": "DELETE {?message ?p ?o} WHERE {?message rdf:type schema:Message ; ?p ?o}", - "forcedBindings": { - "message": { - "type": "uri", - "value": "" - } - } - }, - "STORE_SENT": { - "sparql": "INSERT DATA {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?dateSent}", - "forcedBindings": { - "dateSent": { - "type": "literal", - "value": "" - }, - "message": { - "type": "uri", - "value": "" - }, - "text": { - "type": "literal", - "value": "" - }, - "sender": { - "type": "uri", - "value": "" - }, - "receiver": { - "type": "uri", - "value": "" - } - } - }, - "STORE_RECEIVED": { - "sparql": "INSERT DATA {?message schema:dateReceived ?dateReceived}", - "forcedBindings": { - "dateReceived": { - "type": "literal", - "value": "" - }, - "message": { - "type": "uri", - "value": "" - } - } - }, - "REGISTER_USER": { - "sparql": "DELETE {?x rdf:type schema:Person . ?x schema:name ?userName} WHERE {?x rdf:type schema:Person . ?x schema:name ?userName} ; INSERT {?id rdf:type schema:Person ; schema:name ?userName} WHERE {BIND(IRI(CONCAT(\"http://schema.org/Person-\",STRUUID())) AS ?id)}", - "forcedBindings": { - "userName": { - "type": "literal", - "value": "" - } - } - }, - "DELETE_ALL": { - "sparql": "delete {?s ?p ?o} where {?s ?p ?o}" - } - }, - "queries": { - "SENT": { - "sparql": "SELECT ?message ?sender ?name ?text ?time WHERE {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver ; schema:dateSent ?time . ?sender rdf:type schema:Person ; schema:name ?name . ?receiver rdf:type schema:Person} ORDER BY ?time", - "forcedBindings": { - "receiver": { - "type": "uri", - "value": "" - } - } - }, - "RECEIVED": { - "sparql": "SELECT ?message ?time WHERE {?message schema:sender ?sender ; schema:dateReceived ?time ; rdf:type schema:Message}", - "forcedBindings": { - "sender": { - "type": "uri", - "value": "" - } - } - }, - "LOG_SENT": { - "sparql": "SELECT ?message ?sender ?receiver ?text ?dateSent WHERE {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?dateSent . ?sender rdf:type schema:Person . ?receiver rdf:type schema:Person}" - }, - "LOG_RECEIVED": { - "sparql": "SELECT ?message ?dateReceived WHERE {?message rdf:type schema:Message ; schema:dateReceived ?dateReceived}" - }, - "USERS": { - "sparql": "SELECT ?user ?userName WHERE {?user rdf:type schema:Person ; schema:name ?userName}" - }, - "QUERY_ALL": { - "sparql": "select * where {?s ?p ?o}" - } - } -} diff --git a/tools/client.jpar b/tools/client.jpar deleted file mode 100644 index bee226d2..00000000 --- a/tools/client.jpar +++ /dev/null @@ -1,34 +0,0 @@ -{ - "parameters": { - "host": "localhost", - "ports": { - "http": 8000, - "https": 8443, - "ws": 9000, - "wss": 9443 - }, - "paths": { - "query": "/query", - "update": "/update", - "subscribe": "/subscribe", - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure" - }, - "methods": { - "query": "POST", - "update": "URL_ENCODED_POST" - }, - "formats": { - "query": "JSON", - "update": "HTML" - }, - "security": { - "client_id": "m8gbGvq8dRwV6VGIoxzPM0bakLVYMc11eJKw+6NAYp1ghVOJl9/I8OsDJzpdOYX1", - "client_secret": "qr9vu5K605TPEgpEhU/wc2sTIRGAvkxSQL8CHX5TQz4i8657vZ9VcO5kF/RIKoTP", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfujC9hVz8bZ/EeigQbPBNEoK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYnS3m2uX+Y3difnJn/R6DkmYAo3VRh6SGIGDYAony3ly3GCntrk/9fQzG33/fGU80RLPSs9s0Vtqvv1tzKWQ/x0zE6qKGfEutLnfB0o6f01chrAmucJ3277M1RrtsKjr4dGnerJ48SjU7edkpHttL/ruQV4uEE+9oRlAF8/BcMhrmaFIK/dZpuW0yLpl6PrHl1uXAdFmMitUUyIkyR66aUyumJhxnxWKKQiSyz2k7GzRauNCGSDSElV8HtseqtWF1htPFgOyOVRPInt6GKzS9hse7JXufD0HfKtHnFnEB1bMmowCrscLdIlbqmT17El9DC5q/Xt+5eEUlXHEWv8kBkr/7sX8STB8/KnGZsM/5kWO5Nw5NpiQo1a4DC7u0UV3b85Pa1AH5SmvvF/yk9mFIczm/mFp8xB3HQJbvLD/86yB83TXl19Vfz8B03c3P10dPfKOgF4Ml/DldzABNyAeFdZmugrFKtpn4HV1IvZ4KCPsMvARqflbKZpdv5ly41t4Uao92Ccx1hpUXmBbq6UEwajXaktZuECbJdLsaHisQRe0eDdd3rVuqcqdNhLtlEv0xwVMIhdXqwZLjW4YCL/ghFZrv5DHBFZSBPXyxrQrY/jeqMijENV2vsXE1i4TkYPTCB1UzBX/Fko/GwdO7LAqu/z", - "expires": "N7JJVRJMrmpaP9cjK14Pfw==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - } - } -} \ No newline at end of file diff --git a/tools/mqtt-SOSA-arces2.jsap b/tools/mqtt-SOSA-arces2.jsap deleted file mode 100644 index 4c7813d4..00000000 --- a/tools/mqtt-SOSA-arces2.jsap +++ /dev/null @@ -1,578 +0,0 @@ -{ - "parameters" : { - "host" : "www.vaimee.com" , - "ports" : { - "http" : 8000 , - "https" : 8443 , - "ws" : 9000 , - "wss" : 9443} - , - "paths" : { - "query" : "/query" , - "update" : "/update" , - "subscribe" : "/subscribe" , - "register" : "/oauth/register" , - "tokenRequest" : "/oauth/token" , - "securePath" : "/secure"} - , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} - } - , - "namespaces" : { - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "rdfs" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "sosa" : "http://www.w3.org/ns/sosa/" , - "qudt-1-1" : "http://qudt.org/1.1/schema/qudt#" , - "qudt-unit-1-1" : "http://qudt.org/1.1/vocab/unit#" , - "arces-monitor" : "http://wot.arces.unibo.it/monitor#"} - , - "extended" : { - "simulate" : false , - "mqtt" : { - "url" : "giove.arces.unibo.it" , - "port" : 52877 , - "topics" : ["5CCF7F1B58AC/humidity"] - , - "ssl" : false} - , - "regexTopics" : { - "pepoli:6lowpan:network" : [".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n" , - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n" , - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n"] - } - , - "jsonTopics" : { - "ground/lora/moisture" : { - "id" : "nodeId" , - "value" : "moistureValue"} - } - , - "semantic-mappings" : { - "pepoli/6lowpan/network/NODO1/Temperature" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura sala server Viale Pepoli (rete 6LowPan)" , - "label" : "Temperatura sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO1/Humidity" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo1-Humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità sala server Viale Pepoli (rete 6LowPan)" , - "label" : "Umidità sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO1/Pressure" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo1-Pressure" , - "unit" : "qudt-unit-1-1:Millibar" , - "location" : "arces-monitor:Star" , - "comment" : "Pressione atmosferica sala server Viale Pepoli (rete 6LowPan)" , - "label" : "Pressione atmosferica sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO2/Temperature" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo2-Temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura ufficio Luca Perilli (rete 6LowPan)" , - "label" : "Temperatura ufficio Luca Perilli"} - , - "pepoli/6lowpan/network/NODO2/Humidity" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo2-Humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità ufficio SLuca Perilli (rete 6LowPan)" , - "label" : "Umidità ufficio Luca Perilli"} - , - "pepoli/6lowpan/network/NODO2/Pressure" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo2-Pressure" , - "unit" : "qudt-unit-1-1:Millibar" , - "location" : "arces-monitor:Star" , - "comment" : "Pressione atmosferica ufficio SLuca Perilli (rete 6LowPan)" , - "label" : "Pressione atmosferica sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO3/Temperature" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo3-Temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura Blue Hall (rete 6LowPan)" , - "label" : "Temperatura Blue Hall"} - , - "pepoli/6lowpan/network/NODO3/Humidity" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo3-Humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità Blue Hall (rete 6LowPan)" , - "label" : "Umidità Blue Hall"} - , - "pepoli/6lowpan/network/NODO3/Pressure" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo3-Pressure" , - "unit" : "qudt-unit-1-1:Millibar" , - "location" : "arces-monitor:Star" , - "comment" : "Pressione atmosferica Blue Hall (rete 6LowPan)" , - "label" : "Pressione atmosferica Blue Hall"} - , - "ground/lora/moisture/device1" : { - "observation" : "arces-monitor:ground-lora-moisture-device1" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità giardino 1 (rete LoRa)" , - "label" : "Umidità giardino"} - , - "ground/lora/moisture/device2" : { - "observation" : "arces-monitor:ground-lora-moisture-device2" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità giardino 2 (rete LoRa)" , - "label" : "Umidità giardino"} - , - "arces/servers/ares/ercole/cpu/core-20/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore20" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 20" , - "label" : "Temperatura Server ERCOLE Core 20"} - , - "arces/servers/ares/ercole/cpu/core-19/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore19" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 19" , - "label" : "Temperatura Server ERCOLE Core 19"} - , - "arces/servers/ares/ercole/cpu/core-18/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore18" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 18" , - "label" : "Temperatura Server ERCOLE Core 18"} - , - "arces/servers/ares/ercole/cpu/core-17/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore17" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 17" , - "label" : "Temperatura Server ERCOLE Core 17"} - , - "arces/servers/ares/ercole/cpu/core-16/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore16" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 16" , - "label" : "Temperatura Server ERCOLE Core 16"} - , - "arces/servers/ares/ercole/cpu/core-15/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore15" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 15" , - "label" : "Temperatura Server ERCOLE Core 15"} - , - "arces/servers/ares/ercole/cpu/core-14/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore14" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 14" , - "label" : "Temperatura Server ERCOLE Core 14"} - , - "arces/servers/ares/ercole/cpu/core-13/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore13" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 13" , - "label" : "Temperatura Server ERCOLE Core 13"} - , - "arces/servers/ares/ercole/cpu/core-12/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore12" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 12" , - "label" : "Temperatura Server ERCOLE Core 12"} - , - "arces/servers/ares/ercole/cpu/core-11/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore11" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 11" , - "label" : "Temperatura Server ERCOLE Core 11"} - , - "arces/servers/ares/ercole/cpu/core-10/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore10" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 10" , - "label" : "Temperatura Server ERCOLE Core 10"} - , - "arces/servers/ares/ercole/cpu/core-9/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore9" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 9" , - "label" : "Temperatura Server ERCOLE Core 9"} - , - "arces/servers/ares/ercole/cpu/core-8/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore8" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 8" , - "label" : "Temperatura Server ERCOLE Core 8"} - , - "arces/servers/ares/ercole/cpu/core-7/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore7" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 7" , - "label" : "Temperatura Server ERCOLE Core 7"} - , - "arces/servers/ares/ercole/cpu/core-6/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore6" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 6" , - "label" : "Temperatura Server ERCOLE Core 6"} - , - "arces/servers/ares/ercole/cpu/core-5/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore5" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 5" , - "label" : "Temperatura Server ERCOLE Core 5"} - , - "arces/servers/ares/ercole/cpu/core-4/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore4" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 4" , - "label" : "Temperatura Server ERCOLE Core 4"} - , - "arces/servers/ares/ercole/cpu/core-3/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore3" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 3" , - "label" : "Temperatura Server ERCOLE Core 3"} - , - "arces/servers/ares/ercole/cpu/core-2/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore2" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 2" , - "label" : "Temperatura Server ERCOLE Core 2"} - , - "arces/servers/ares/ercole/cpu/core-1/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore1" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 1" , - "label" : "Temperatura Server ERCOLE Core 1"} - , - "arces/servers/mars/mml/cpu/core-6/temperature" : { - "observation" : "arces-monitor:ServerMml6" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 6" , - "label" : "Temperatura Server MML Core 6"} - , - "arces/servers/mars/mml/cpu/core-5/temperature" : { - "observation" : "arces-monitor:ServerMml5" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 5" , - "label" : "Temperatura Server MML Core 5"} - , - "arces/servers/mars/mml/cpu/core-4/temperature" : { - "observation" : "arces-monitor:ServerMml4" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 4" , - "label" : "Temperatura Server MML Core 4"} - , - "arces/servers/mars/mml/cpu/core-3/temperature" : { - "observation" : "arces-monitor:ServerMml3" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 3" , - "label" : "Temperatura Server MML Core 3"} - , - "arces/servers/mars/mml/cpu/core-2/temperature" : { - "observation" : "arces-monitor:ServerMml2" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 2" , - "label" : "Temperatura Server MML Core 2"} - , - "arces/servers/mars/mml/cpu/core-1/temperature" : { - "observation" : "arces-monitor:ServerMml1" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 1" , - "label" : "Temperatura Server MML Core 1"} - , - "arces/servers/mars/giove/cpu/core-6/temperature" : { - "observation" : "arces-monitor:ServerGiove6" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 6" , - "label" : "Temperatura Server GIOVE Core 6"} - , - "arces/servers/mars/giove/cpu/core-5/temperature" : { - "observation" : "arces-monitor:ServerGiove5" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 5" , - "label" : "Temperatura Server GIOVE Core 5"} - , - "arces/servers/mars/giove/cpu/core-4/temperature" : { - "observation" : "arces-monitor:ServerGiove4" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 4" , - "label" : "Temperatura Server GIOVE Core 4"} - , - "arces/servers/mars/giove/cpu/core-3/temperature" : { - "observation" : "arces-monitor:ServerGiove3" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 3" , - "label" : "Temperatura Server GIOVE Core 3"} - , - "arces/servers/mars/giove/cpu/core-2/temperature" : { - "observation" : "arces-monitor:ServerGiove2" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 2" , - "label" : "Temperatura Server GIOVE Core 2"} - , - "arces/servers/mars/giove/cpu/core-1/temperature" : { - "observation" : "arces-monitor:ServerGiove1" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 1" , - "label" : "Temperatura Server GIOVE Core 1"} - , - "arces/servers/mars/mml/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerMmlHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML HDD SDA" , - "label" : "Temperatura Server MML HDD SDA"} - , - "arces/servers/mars/mml/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerMmlHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML HDD SDB" , - "label" : "Temperatura Server MML HDD SDB"} - , - "arces/servers/mars/giove/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDA" , - "label" : "Temperatura Server GIOVE HDD SDA"} - , - "arces/servers/mars/giove/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDB" , - "label" : "Temperatura Server GIOVE HDD SDB"} - , - "arces/servers/mars/giove/hd/sdc/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdc" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDC" , - "label" : "Temperatura Server GIOVE HDD SDC"} - , - "arces/servers/mars/giove/hd/sdd/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdd" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDD" , - "label" : "Temperatura Server GIOVE HDD SDD"} - , - "arces/servers/mars/giove/hd/sdf/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdf" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDF" , - "label" : "Temperatura Server GIOVE HDD SDF"} - , - "arces/servers/mars/giove/hd/sdg/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdg" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDG" , - "label" : "Temperatura Server GIOVE HDD SDG"} - , - "arces/servers/mars/giove/hd/sdh/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdh" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDH" , - "label" : "Temperatura Server GIOVE HDD SDH"} - , - "arces/servers/mars/marsamba/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerSambaHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server SAMBA HDD SDA" , - "label" : "Temperatura Server SAMBA HDD SDA"} - , - "arces/servers/mars/marsamba/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerSambaHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server SAMBA HDD SDB" , - "label" : "Temperatura Server SAMBA HDD SDB"} - , - "arces/servers/ares/ercole/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerErcoleHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE HDD SDA" , - "label" : "Temperatura Server ERCOLE HDD SDA"} - , - "arces/servers/ares/ercole/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerErcoleHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE HDD SDB" , - "label" : "Temperatura Server ERCOLE HDD SDB"} - , - "5CCF7F15676D/temperature" : { - "observation" : "arces-monitor:5CCF7F15676D-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura zona rack sala server Toffano" , - "label" : "Temperatura zona rack"} - , - "5CCF7F15676D/humidity" : { - "observation" : "arces-monitor:5CCF7F15676D-humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Mars" , - "comment" : "Umidità zona rack sala server Toffano" , - "label" : "Umidità zona rack"} - , - "5CCF7F1B599E/temperature" : { - "observation" : "arces-monitor:5CCF7F1B599E-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura zona finestra sala server Toffano" , - "label" : "Temperatura zona finestra"} - , - "5CCF7F1B599E/humidity" : { - "observation" : "arces-monitor:5CCF7F1B599E-humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Mars" , - "comment" : "Umidità zona finestra sala server Toffano" , - "label" : "Umidità zona finestra"} - , - "5CCF7F151DC9/temperature" : { - "observation" : "arces-monitor:5CCF7F151DC9-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura esterna sala server Toffano" , - "label" : "Temperatura esterna"} - , - "5CCF7F1B58AC/temperature" : { - "observation" : "arces-monitor:5CCF7F1B58AC-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura zona rack sala server Pepoli" , - "label" : "Temperatura zona rack"} - , - "5CCF7F1B58AC/humidity" : { - "observation" : "arces-monitor:5CCF7F1B58AC-humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità zona rack sala server Pepoli" , - "label" : "Umidità zona rack"} - } - } - , - "updates" : { - "ADD_OBSERVATION" : { - "sparql" : "DELETE {?observation ?p ?o . ?q ?p1 ?o1} INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit} WHERE {BIND(IRI(CONCAT(\"arces-monitor:QuantityValue-\",STRUUID())) AS ?quantity) . OPTIONAL {?observation rdf:type sosa:Observation ; ?p ?o ; sosa:hasResult ?q . ?q ?p1 ?o1 }}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - , - "comment" : { - "type" : "literal" , - "value" : ""} - , - "label" : { - "type" : "literal" , - "value" : ""} - , - "location" : { - "type" : "uri" , - "value" : ""} - , - "topic" : { - "type" : "literal" , - "value" : ""}, - "unit" : { - "type" : "uri" , - "value" : ""} - - } - } - , - "UPDATE_OBSERVATION_VALUE" : { - "sparql" : "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - } - , - "queries" : { - "OBSERVATIONS_TOPICS" : { - "sparql" : "SELECT ?observation ?topic WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}"} - , - "OBSERVATIONS_VALUES" : { - "sparql" : " SELECT ?observation ?location ?quantity ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}"} - , - "OBSERVATIONS_VALUES_BY_LOCATION" : { - "sparql" : " SELECT ?label ?value ?unit WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "location" : { - "type" : "uri" , - "value" : ""} - } - } - , - "OBSERVATIONS_VALUES_BY_UNIT" : { - "sparql" : " SELECT ?location ?label ?value WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "unit" : { - "type" : "uri" , - "value" : ""} - } - } - , - "OBSERVATION" : { - "sparql" : " SELECT ?location ?label ?quantity ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - } - } - } -} diff --git a/tools/mqtt-SOSA.jsap b/tools/mqtt-SOSA.jsap deleted file mode 100644 index e28cf01b..00000000 --- a/tools/mqtt-SOSA.jsap +++ /dev/null @@ -1,195 +0,0 @@ -{ - "parameters" : { - "host" : "www.vaimee.com" , - "ports" : { - "http" : 8000 , - "https" : 8443 , - "ws" : 9000 , - "wss" : 9443} - , - "paths" : { - "query" : "/query" , - "update" : "/update" , - "subscribe" : "/subscribe" , - "register" : "/oauth/register" , - "tokenRequest" : "/oauth/token" , - "securePath" : "/secure"} - , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} - } - , - "namespaces" : { - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "rdfs" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "sosa" : "http://www.w3.org/ns/sosa/" , - "qudt-1-1" : "http://qudt.org/1.1/schema/qudt#" , - "qudt-unit-1-1" : "http://qudt.org/1.1/vocab/unit#" , - "arces-monitor" : "http://wot.arces.unibo.it/monitor#" , - "time" : "http://www.w3.org/2006/time#" , - "xsd" : "http://www.w3.org/2001/XMLSchema#"} - , - "extended" : { - "simulate" : false , - "mqtt" : { - "url" : "iot.eclipse.org" , - "port" : 1883 , - "topics" : ["FisherHouse/#"] - , - "ssl" : false} - , - "regexTopics" : { - "pepoli:6lowpan:network" : [".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n" , - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n" , - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n"] - } - , - "jsonTopics" : { - "ground/lora/moisture" : { - "id" : "nodeId" , - "value" : "moistureValue"} - } - , - "semantic-mappings" : { - "FisherHouse/Cabin/Garage/Up/Temp/Set" : { - "observation" : "arces-monitor:FisherHouse-Cabin-Garage-Up-Temp-Set" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "FisherHouse-Cabin-Garage-Up-Temp-Set"} - , - "FisherHouse/CurrentTimestamp" : { - "observation" : "arces-monitor:FisherHouse-CurrentTimestamp" , - "unit" : "time:Instant" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "FisherHouse-CurrentTimestamp"} - , - "FisherHouse/Cabin/Garage/Up/Heat" : { - "observation" : "arces-monitor:FisherHouse-Cabin-Garage-Up-Heat" , - "unit" : "xsd:decimal" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "FisherHouse-Cabin-Garage-Up-Heat"} - , - "FisherHouse/Cabin/Garage/Down/Temp/Meas" : { - "observation" : "arces-monitor:FisherHouse-Cabin-Garage-Down-Temp-Meas" , - "unit" : "xsd:float" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "FisherHouse-Cabin-Garage-Down-Temp-Meas"} - , - "FisherHouse/Cabin/Garage/Door/State" : { - "observation" : "arces-monitor:FisherHouse-Cabin-Garage-Door-State" , - "unit" : "xsd:decimal" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "FisherHouse-Cabin-Garage-Door-State"} - , - "FisherHouse/Cabin/Garage/Door/Distance" : { - "observation" : "arces-monitor:FisherHouse-Cabin-Garage-Door-Distance" , - "unit" : "xsd:decimal" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "FisherHouse-Cabin-Garage-Door-Distance"} - , - "FisherHouse/RSSI/GarageDoor" : { - "observation" : "arces-monitor:FisherHouse-RSSI-GarageDoor" , - "unit" : "xsd:decimal" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "RSSI garage door"} - , - "FisherHouse/House/Wind/Direction/Meas" : { - "observation" : "arces-monitor:FisherHouse/House/Wind/Direction/Meas" , - "unit" : "xsd:decimal" , - "location" : "arces-monitor:FisherHouse" , - "comment" : "Data coming from iot.eclipse.org" , - "label" : "Wind direction"} - } - } - , - "updates" : { - "ADD_OBSERVATION" : { - "sparql" : "INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit} WHERE {BIND(IRI(CONCAT(\"arces-monitor:QuantityValue-\",STRUUID())) AS ?quantity) . FILTER NOT EXISTS {?observation rdf:type sosa:Observation}}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - , - "comment" : { - "type" : "literal" , - "value" : ""} - , - "label" : { - "type" : "literal" , - "value" : ""} - , - "location" : { - "type" : "uri" , - "value" : ""} - , - "topic" : { - "type" : "literal" , - "value" : ""} - , - "unit" : { - "type" : "uri" , - "value" : ""} - } - } - , - "UPDATE_OBSERVATION_VALUE" : { - "sparql" : "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - } - , - "queries" : { - "ADD_OBSERVATION" : { - "sparql" : "SELECT ?observation ?topic WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}"} - , - "OBSERVATIONS" : { - "sparql" : " SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}"} - , - "OBSERVATIONS_BY_LOCATION" : { - "sparql" : " SELECT ?label ?value ?unit WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "location" : { - "type" : "uri" , - "value" : ""} - } - } - , - "OBSERVATIONS_BY_UNIT" : { - "sparql" : " SELECT ?location ?label ?value WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "unit" : { - "type" : "uri" , - "value" : ""} - } - } - , - "ALL_VALUES" : { - "sparql" : " SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - } - } - } -} diff --git a/tools/mqtt.jsap b/tools/mqtt.jsap deleted file mode 100644 index 74e21be3..00000000 --- a/tools/mqtt.jsap +++ /dev/null @@ -1,626 +0,0 @@ -{ - "host": "mml.arces.unibo.it", - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/subscribe" - } - } - }, - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - }, - "namespaces": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "sosa": "http://www.w3.org/ns/sosa/", - "qudt-1-1": "http://qudt.org/1.1/schema/qudt#", - "qudt-unit-1-1": "http://qudt.org/1.1/vocab/unit#", - "arces-monitor": "http://wot.arces.unibo.it/monitor#", - "mqtt": "http://wot.arces.unibo.it/mqtt#" - }, - "extended": { - "simulate": false, - "mqtt": { - "url": "test.mosquitto.org", - "port": 1883, - "topics": [ - "#" - ], - "ssl": false - }, - "regexTopics": { - "pepoli:6lowpan:network": [ - ".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n", - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n", - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n" - ] - }, - "jsonTopics": { - "ground/lora/moisture": { - "id": "nodeId", - "value": "moistureValue" - } - }, - "semantic-mappings": { - "pepoli/6lowpan/network/NODO1/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura sala server Viale Pepoli (rete 6LowPan)", - "label": "Temperatura sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO1/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità sala server Viale Pepoli (rete 6LowPan)", - "label": "Umidità sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO1/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica sala server Viale Pepoli (rete 6LowPan)", - "label": "Pressione atmosferica sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO2/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura ufficio Luca Perilli (rete 6LowPan)", - "label": "Temperatura ufficio Luca Perilli" - }, - "pepoli/6lowpan/network/NODO2/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità ufficio SLuca Perilli (rete 6LowPan)", - "label": "Umidità ufficio Luca Perilli" - }, - "pepoli/6lowpan/network/NODO2/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica ufficio SLuca Perilli (rete 6LowPan)", - "label": "Pressione atmosferica sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO3/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura Blue Hall (rete 6LowPan)", - "label": "Temperatura Blue Hall" - }, - "pepoli/6lowpan/network/NODO3/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità Blue Hall (rete 6LowPan)", - "label": "Umidità Blue Hall" - }, - "pepoli/6lowpan/network/NODO3/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica Blue Hall (rete 6LowPan)", - "label": "Pressione atmosferica Blue Hall" - }, - "ground/lora/moisture/device1": { - "observation": "arces-monitor:ground-lora-moisture-device1", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità giardino 1 (rete LoRa)", - "label": "Umidità giardino" - }, - "ground/lora/moisture/device2": { - "observation": "arces-monitor:ground-lora-moisture-device2", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità giardino 2 (rete LoRa)", - "label": "Umidità giardino" - }, - "arces/servers/ares/ercole/cpu/core-20/temperature": { - "observation": "arces-monitor:ServerErcoleCore20", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 20", - "label": "Temperatura Server ERCOLE Core 20" - }, - "arces/servers/ares/ercole/cpu/core-19/temperature": { - "observation": "arces-monitor:ServerErcoleCore19", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 19", - "label": "Temperatura Server ERCOLE Core 19" - }, - "arces/servers/ares/ercole/cpu/core-18/temperature": { - "observation": "arces-monitor:ServerErcoleCore18", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 18", - "label": "Temperatura Server ERCOLE Core 18" - }, - "arces/servers/ares/ercole/cpu/core-17/temperature": { - "observation": "arces-monitor:ServerErcoleCore17", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 17", - "label": "Temperatura Server ERCOLE Core 17" - }, - "arces/servers/ares/ercole/cpu/core-16/temperature": { - "observation": "arces-monitor:ServerErcoleCore16", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 16", - "label": "Temperatura Server ERCOLE Core 16" - }, - "arces/servers/ares/ercole/cpu/core-15/temperature": { - "observation": "arces-monitor:ServerErcoleCore15", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 15", - "label": "Temperatura Server ERCOLE Core 15" - }, - "arces/servers/ares/ercole/cpu/core-14/temperature": { - "observation": "arces-monitor:ServerErcoleCore14", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 14", - "label": "Temperatura Server ERCOLE Core 14" - }, - "arces/servers/ares/ercole/cpu/core-13/temperature": { - "observation": "arces-monitor:ServerErcoleCore13", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 13", - "label": "Temperatura Server ERCOLE Core 13" - }, - "arces/servers/ares/ercole/cpu/core-12/temperature": { - "observation": "arces-monitor:ServerErcoleCore12", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 12", - "label": "Temperatura Server ERCOLE Core 12" - }, - "arces/servers/ares/ercole/cpu/core-11/temperature": { - "observation": "arces-monitor:ServerErcoleCore11", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 11", - "label": "Temperatura Server ERCOLE Core 11" - }, - "arces/servers/ares/ercole/cpu/core-10/temperature": { - "observation": "arces-monitor:ServerErcoleCore10", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 10", - "label": "Temperatura Server ERCOLE Core 10" - }, - "arces/servers/ares/ercole/cpu/core-9/temperature": { - "observation": "arces-monitor:ServerErcoleCore9", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 9", - "label": "Temperatura Server ERCOLE Core 9" - }, - "arces/servers/ares/ercole/cpu/core-8/temperature": { - "observation": "arces-monitor:ServerErcoleCore8", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 8", - "label": "Temperatura Server ERCOLE Core 8" - }, - "arces/servers/ares/ercole/cpu/core-7/temperature": { - "observation": "arces-monitor:ServerErcoleCore7", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 7", - "label": "Temperatura Server ERCOLE Core 7" - }, - "arces/servers/ares/ercole/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerErcoleCore6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 6", - "label": "Temperatura Server ERCOLE Core 6" - }, - "arces/servers/ares/ercole/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerErcoleCore5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 5", - "label": "Temperatura Server ERCOLE Core 5" - }, - "arces/servers/ares/ercole/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerErcoleCore4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 4", - "label": "Temperatura Server ERCOLE Core 4" - }, - "arces/servers/ares/ercole/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerErcoleCore3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 3", - "label": "Temperatura Server ERCOLE Core 3" - }, - "arces/servers/ares/ercole/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerErcoleCore2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 2", - "label": "Temperatura Server ERCOLE Core 2" - }, - "arces/servers/ares/ercole/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerErcoleCore1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 1", - "label": "Temperatura Server ERCOLE Core 1" - }, - "arces/servers/mars/mml/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerMml6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 6", - "label": "Temperatura Server MML Core 6" - }, - "arces/servers/mars/mml/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerMml5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 5", - "label": "Temperatura Server MML Core 5" - }, - "arces/servers/mars/mml/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerMml4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 4", - "label": "Temperatura Server MML Core 4" - }, - "arces/servers/mars/mml/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerMml3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 3", - "label": "Temperatura Server MML Core 3" - }, - "arces/servers/mars/mml/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerMml2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 2", - "label": "Temperatura Server MML Core 2" - }, - "arces/servers/mars/mml/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerMml1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 1", - "label": "Temperatura Server MML Core 1" - }, - "arces/servers/mars/giove/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerGiove6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 6", - "label": "Temperatura Server GIOVE Core 6" - }, - "arces/servers/mars/giove/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerGiove5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 5", - "label": "Temperatura Server GIOVE Core 5" - }, - "arces/servers/mars/giove/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerGiove4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 4", - "label": "Temperatura Server GIOVE Core 4" - }, - "arces/servers/mars/giove/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerGiove3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 3", - "label": "Temperatura Server GIOVE Core 3" - }, - "arces/servers/mars/giove/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerGiove2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 2", - "label": "Temperatura Server GIOVE Core 2" - }, - "arces/servers/mars/giove/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerGiove1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 1", - "label": "Temperatura Server GIOVE Core 1" - }, - "arces/servers/mars/mml/hd/sda/temperature": { - "observation": "arces-monitor:ServerMmlHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML HDD SDA", - "label": "Temperatura Server MML HDD SDA" - }, - "arces/servers/mars/mml/hd/sdb/temperature": { - "observation": "arces-monitor:ServerMmlHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML HDD SDB", - "label": "Temperatura Server MML HDD SDB" - }, - "arces/servers/mars/giove/hd/sda/temperature": { - "observation": "arces-monitor:ServerGioveHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDA", - "label": "Temperatura Server GIOVE HDD SDA" - }, - "arces/servers/mars/giove/hd/sdb/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDB", - "label": "Temperatura Server GIOVE HDD SDB" - }, - "arces/servers/mars/giove/hd/sdc/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdc", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDC", - "label": "Temperatura Server GIOVE HDD SDC" - }, - "arces/servers/mars/giove/hd/sdd/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdd", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDD", - "label": "Temperatura Server GIOVE HDD SDD" - }, - "arces/servers/mars/giove/hd/sdf/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdf", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDF", - "label": "Temperatura Server GIOVE HDD SDF" - }, - "arces/servers/mars/giove/hd/sdg/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdg", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDG", - "label": "Temperatura Server GIOVE HDD SDG" - }, - "arces/servers/mars/giove/hd/sdh/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdh", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDH", - "label": "Temperatura Server GIOVE HDD SDH" - }, - "arces/servers/mars/marsamba/hd/sda/temperature": { - "observation": "arces-monitor:ServerSambaHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server SAMBA HDD SDA", - "label": "Temperatura Server SAMBA HDD SDA" - }, - "arces/servers/mars/marsamba/hd/sdb/temperature": { - "observation": "arces-monitor:ServerSambaHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server SAMBA HDD SDB", - "label": "Temperatura Server SAMBA HDD SDB" - }, - "arces/servers/ares/ercole/hd/sda/temperature": { - "observation": "arces-monitor:ServerErcoleHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE HDD SDA", - "label": "Temperatura Server ERCOLE HDD SDA" - }, - "arces/servers/ares/ercole/hd/sdb/temperature": { - "observation": "arces-monitor:ServerErcoleHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE HDD SDB", - "label": "Temperatura Server ERCOLE HDD SDB" - }, - "5CCF7F15676D/temperature": { - "observation": "arces-monitor:5CCF7F15676D-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura zona rack sala server Toffano", - "label": "Temperatura zona rack" - }, - "5CCF7F15676D/humidity": { - "observation": "arces-monitor:5CCF7F15676D-humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Mars", - "comment": "Umidità zona rack sala server Toffano", - "label": "Umidità zona rack" - }, - "5CCF7F1B599E/temperature": { - "observation": "arces-monitor:5CCF7F1B599E-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura zona finestra sala server Toffano", - "label": "Temperatura zona finestra" - }, - "5CCF7F1B599E/humidity": { - "observation": "arces-monitor:5CCF7F1B599E-humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Mars", - "comment": "Umidità zona finestra sala server Toffano", - "label": "Umidità zona finestra" - }, - "5CCF7F151DC9/temperature": { - "observation": "arces-monitor:5CCF7F151DC9-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura esterna sala server Toffano", - "label": "Temperatura esterna" - }, - "5CCF7F1B58AC/temperature": { - "observation": "arces-monitor:5CCF7F1B58AC-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura zona rack sala server Pepoli", - "label": "Temperatura zona rack" - }, - "5CCF7F1B58AC/humidity": { - "observation": "arces-monitor:5CCF7F1B58AC-humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità zona rack sala server Pepoli", - "label": "Umidità zona rack" - } - } - }, - "updates": { - "MQTT_MESSAGE": { - "sparql": "INSERT {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?timestamp} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Message-\",STRUUID())) AS ?message) . BIND(now() AS ?timestamp)}", - "forcedBindings": { - "value": { - "type": "literal", - "value": "" - }, - "topic": { - "type": "literal", - "value": "" - }, - "broker": { - "type": "literal", - "value": "" - } - } - }, - "ADD_OBSERVATION": { - "sparql": "DELETE {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity. ?quantity qudt-1-1:numericValue ?value}; DELETE {?observation rdf:type sosa:Observation ; rdfs:label ?X ; sosa:hasFeatureOfInterest ?locati ; sosa:hasResult ?quantityOLD . ?quantityOLD rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unitOLD} WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?labelOLD ; sosa:hasFeatureOfInterest ?locationOLD ; sosa:hasResult ?quantityOLD . ?quantityOLD rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unitOLD};INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity)}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "" - }, - "comment": { - "type": "literal", - "value": "" - }, - "label": { - "type": "literal", - "value": "" - }, - "location": { - "type": "uri", - "value": "" - }, - "topic": { - "type": "literal", - "value": "" - }, - "unit": { - "type": "uri", - "value": "" - } - } - }, - "UPDATE_OBSERVATION_VALUE": { - "sparql": "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "" - }, - "value": { - "type": "literal", - "value": "" - } - } - } - }, - "queries": { - "OBSERVATIONS_TOPICS": { - "sparql": "SELECT ?observation ?topic WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}" - }, - "OBSERVATIONS": { - "sparql": " SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" - }, - "OBSERVATIONS_BY_LOCATION": { - "sparql": " SELECT ?label ?value ?unit WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings": { - "location": { - "type": "uri", - "value": "" - } - } - }, - "OBSERVATIONS_BY_UNIT": { - "sparql": " SELECT ?location ?label ?value WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings": { - "unit": { - "type": "uri", - "value": "" - } - } - }, - "ALL_VALUES": { - "sparql": " SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "" - } - } - }, - "MQTT_TOPICS_COUNT": { - "sparql": "SELECT (COUNT(DISTINCT ?topic) AS ?topics) WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic}" - }, - "MQTT_TOPICS": { - "sparql": "SELECT DISTINCT ?broker ?topic WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" - }, - "MQTT_TOPIC_VALUE": { - "sparql": "SELECT ?value WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", - "forcedBindings": { - "topic": { - "type": "literal", - "value": "" - } - } - }, - "MQTT_MESSAGES": { - "sparql": "SELECT ?broker ?topic ?value WHERE {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" - } - } -} diff --git a/tools/mqttAdapter.jsap b/tools/mqttAdapter.jsap deleted file mode 100644 index ef3a4971..00000000 --- a/tools/mqttAdapter.jsap +++ /dev/null @@ -1,160 +0,0 @@ -{ - "parameters" : { - "host" : "localhost" , - "concurrentRequests" : 5 , - "ports" : { - "http" : 8000 , - "https" : 8443 , - "ws" : 9000 , - "wss" : 9443} - , - "paths" : { - "query" : "/query" , - "update" : "/update" , - "subscribe" : "/subscribe" , - "register" : "/oauth/register" , - "tokenRequest" : "/oauth/token" , - "securePath" : "/secure"} - , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} - , - "security" : { - "client_id" : "5j7b8RUD3XE5SZoDrFKt2Xn68MYm6maG6I67+0Qk0KPsFsBUz8L71kxS0UBoU19Q" , - "client_secret" : "R1n59N8vTzxof3fuNeaPJL3uPWGTUzoatukGlqlmYlu2l0fg2MtRndEzmpujpGI/" , - "jwt" : "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfuo5pr6yu/dNILNOt8hd9jqK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYn8Ou/nH3c50k/Cs9LEm6iaQxbvHRvHgzAm0mAuwcEC6svdou2VqEqBhkLLURlIu2AtRCzMQCFC+Tcnnah3e0NCH05W8Mdatp0+nMeL+BmdR5u5LEQTCy7d3YTWoC8i4uAPJ5oEEgpoke2PA29KvKo6z6yA/CxN4Qezrp+XIizBXAenHEZoycDFGMMhg2KVMoWLTOEaXSNUJOtY6BuNVl18dBgGJuESbICeWuEtou+YwpWizUV8QWeIuJlUTUn597TKbntBrNEHW/vXzfPO8Ydru7usw60NeExL9gvpAEQXDMv0Q8vRJVtDrQZDR++hbr7UV35qcIBFW3PzOt0DP6/TGcqFyVCHUA2QYKwYezGdCmb582vCMwsI6kVCSXzvz5lhBNankJ6icCsm5elY39EsRKTCQPidAxyY+1JAjrmBYoshPc594N/wbZt2pTgVQvtIAZmL1k48QVNDBymz8jJjlcJlB+3Doly0YwqxjCUZhtsPCJ8tRt0hGt21QjCoRE1LfdkdKBci9Afse49SCMizoiVK/nJlA2lDPmQCwVrEbT5LUmtDhAhZAePtrRPF9r9fRCKRR1NKqtqHXRXG4VaAvQJKvAJCbJVxiOrDn+UAD9p290XKYfyQ3+usiX4C1NxTTH89MiDHkbAAju0yNksS" , - "expires" : "J1dLQZDlDYdWtEaYSIG5+A==" , - "type" : "XPrHEX2xHy+5IuXHPHigMw=="} - } - , - "namespaces" : { - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "rdfs" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "mqtt" : "http://wot.arces.unibo.it/mqtt#"} - , - "extended" : { - "mqtt" : { - "url" : "giove.arces.unibo.it" , - "port" : 52877 , - "topics" : ["#"] - , - "ssl" : false} - } - , - "updates" : { - "UPDATE" : { - "sparql" : "DELETE {?node mqtt:hasValue ?oldValue } INSERT {?node rdf:type mqtt:Node ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker } WHERE { {BIND(IRI(CONCAT(\"mqtt:Node-\",STRUUID())) AS ?node) . FILTER NOT EXISTS {?nodeOld rdf:type mqtt:Node ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker } } UNION { ?node mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:hasValue ?oldValue ; rdf:type mqtt:Node } }" , - "forcedBindings" : { - "value" : { - "type" : "literal" , - "value" : ""} - , - "topic" : { - "type" : "literal" , - "value" : ""} - , - "broker" : { - "type" : "literal" , - "value" : ""} - } - }, - "ADD_OBSERVATION":{ - "sparql":"INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit} WHERE {BIND(IRI(CONCAT(\"arces-monitor:QuantityValue-\",STRUUID())) AS ?quantity) . FILTER NOT EXISTS {?observation rdf:type sosa:Observation}}", - "forcedBindings":{ - "observation":{ - "type":"uri", - "value":""} - , - "comment":{ - "type":"literal", - "value":""} - , - "label":{ - "type":"literal", - "value":""} - , - "location":{ - "type":"uri", - "value":""} - , - "topic":{ - "type":"literal", - "value":""} - , - "unit":{ - "type":"uri", - "value":""} - } - } - , - "UPDATE_OBSERVATION_VALUE":{ - "sparql":"DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}", - "forcedBindings":{ - "observation":{ - "type":"uri", - "value":""} - , - "value":{ - "type":"literal", - "value":""} - } - } - } - , - "queries" : { - "ADD_OBSERVATION":{ - "sparql":"SELECT ?observation ?topic WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}"} - , - "OBSERVATIONS":{ - "sparql":" SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}"} - , - "OBSERVATIONS_BY_LOCATION":{ - "sparql":" SELECT ?label ?value ?unit WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings":{ - "location":{ - "type":"uri", - "value":""} - } - } - , - "OBSERVATIONS_BY_UNIT":{ - "sparql":" SELECT ?location ?label ?value WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings":{ - "unit":{ - "type":"uri", - "value":""} - } - } - , - "ALL_VALUES":{ - "sparql":" SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings":{ - "observation":{ - "type":"uri", - "value":""} - } - }, - "COUNT_TOPICS" : { - "sparql" : "SELECT (COUNT(?topic) AS ?topics) WHERE {?node rdf:type mqtt:Node ; mqtt:hasTopic ?topic}"} - , - "TOPIC" : { - "sparql" : "SELECT ?topic WHERE {?node rdf:type mqtt:Node ; mqtt:hasTopic ?topic}"} - , - "VALUE" : { - "sparql" : "SELECT ?value WHERE {?node rdf:type mqtt:Node ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}" , - "forcedBindings" : { - "topic" : { - "type" : "literal" , - "value" : ""} - } - } - , - "NODE" : { - "sparql" : "SELECT ?topic ?value WHERE {?node rdf:type mqtt:Node ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic}"} - } -} diff --git a/tools/mqttMonitor.jsap b/tools/mqttMonitor.jsap deleted file mode 100644 index f1847b7a..00000000 --- a/tools/mqttMonitor.jsap +++ /dev/null @@ -1,578 +0,0 @@ -{ - "parameters" : { - "host" : "www.vaimee.com" , - "ports" : { - "http" : 8000 , - "https" : 8443 , - "ws" : 9000 , - "wss" : 9443} - , - "paths" : { - "query" : "/query" , - "update" : "/update" , - "subscribe" : "/subscribe" , - "register" : "/oauth/register" , - "tokenRequest" : "/oauth/token" , - "securePath" : "/secure"} - , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} - } - , - "namespaces" : { - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "rdfs" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" , - "sosa" : "http://www.w3.org/ns/sosa/" , - "qudt-1-1" : "http://qudt.org/1.1/schema/qudt#" , - "qudt-unit-1-1" : "http://qudt.org/1.1/vocab/unit#" , - "arces-monitor" : "http://wot.arces.unibo.it/monitor#"} - , - "extended" : { - "simulate" : false , - "mqtt" : { - "url" : "giove.arces.unibo.it" , - "port" : 52877 , - "topics" : ["#"] - , - "ssl" : false} - , - "regexTopics" : { - "pepoli:6lowpan:network" : [".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n" , - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n" , - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n"] - } - , - "jsonTopics" : { - "ground/lora/moisture" : { - "id" : "nodeId" , - "value" : "moistureValue"} - } - , - "semantic-mappings" : { - "pepoli/6lowpan/network/NODO1/Temperature" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura sala server Viale Pepoli (rete 6LowPan)" , - "label" : "Temperatura sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO1/Humidity" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo1-Humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità sala server Viale Pepoli (rete 6LowPan)" , - "label" : "Umidità sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO1/Pressure" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo1-Pressure" , - "unit" : "qudt-unit-1-1:Millibar" , - "location" : "arces-monitor:Star" , - "comment" : "Pressione atmosferica sala server Viale Pepoli (rete 6LowPan)" , - "label" : "Pressione atmosferica sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO2/Temperature" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo2-Temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura ufficio Luca Perilli (rete 6LowPan)" , - "label" : "Temperatura ufficio Luca Perilli"} - , - "pepoli/6lowpan/network/NODO2/Humidity" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo2-Humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità ufficio SLuca Perilli (rete 6LowPan)" , - "label" : "Umidità ufficio Luca Perilli"} - , - "pepoli/6lowpan/network/NODO2/Pressure" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo2-Pressure" , - "unit" : "qudt-unit-1-1:Millibar" , - "location" : "arces-monitor:Star" , - "comment" : "Pressione atmosferica ufficio SLuca Perilli (rete 6LowPan)" , - "label" : "Pressione atmosferica sala server Viale Pepoli"} - , - "pepoli/6lowpan/network/NODO3/Temperature" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo3-Temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura Blue Hall (rete 6LowPan)" , - "label" : "Temperatura Blue Hall"} - , - "pepoli/6lowpan/network/NODO3/Humidity" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo3-Humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità Blue Hall (rete 6LowPan)" , - "label" : "Umidità Blue Hall"} - , - "pepoli/6lowpan/network/NODO3/Pressure" : { - "observation" : "arces-monitor:Pepoli-6lowpan-Nodo3-Pressure" , - "unit" : "qudt-unit-1-1:Millibar" , - "location" : "arces-monitor:Star" , - "comment" : "Pressione atmosferica Blue Hall (rete 6LowPan)" , - "label" : "Pressione atmosferica Blue Hall"} - , - "ground/lora/moisture/device1" : { - "observation" : "arces-monitor:ground-lora-moisture-device1" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità giardino 1 (rete LoRa)" , - "label" : "Umidità giardino"} - , - "ground/lora/moisture/device2" : { - "observation" : "arces-monitor:ground-lora-moisture-device2" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità giardino 2 (rete LoRa)" , - "label" : "Umidità giardino"} - , - "arces/servers/ares/ercole/cpu/core-20/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore20" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 20" , - "label" : "Temperatura Server ERCOLE Core 20"} - , - "arces/servers/ares/ercole/cpu/core-19/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore19" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 19" , - "label" : "Temperatura Server ERCOLE Core 19"} - , - "arces/servers/ares/ercole/cpu/core-18/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore18" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 18" , - "label" : "Temperatura Server ERCOLE Core 18"} - , - "arces/servers/ares/ercole/cpu/core-17/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore17" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 17" , - "label" : "Temperatura Server ERCOLE Core 17"} - , - "arces/servers/ares/ercole/cpu/core-16/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore16" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 16" , - "label" : "Temperatura Server ERCOLE Core 16"} - , - "arces/servers/ares/ercole/cpu/core-15/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore15" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 15" , - "label" : "Temperatura Server ERCOLE Core 15"} - , - "arces/servers/ares/ercole/cpu/core-14/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore14" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 14" , - "label" : "Temperatura Server ERCOLE Core 14"} - , - "arces/servers/ares/ercole/cpu/core-13/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore13" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 13" , - "label" : "Temperatura Server ERCOLE Core 13"} - , - "arces/servers/ares/ercole/cpu/core-12/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore12" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 12" , - "label" : "Temperatura Server ERCOLE Core 12"} - , - "arces/servers/ares/ercole/cpu/core-11/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore11" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 11" , - "label" : "Temperatura Server ERCOLE Core 11"} - , - "arces/servers/ares/ercole/cpu/core-10/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore10" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 10" , - "label" : "Temperatura Server ERCOLE Core 10"} - , - "arces/servers/ares/ercole/cpu/core-9/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore9" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 9" , - "label" : "Temperatura Server ERCOLE Core 9"} - , - "arces/servers/ares/ercole/cpu/core-8/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore8" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 8" , - "label" : "Temperatura Server ERCOLE Core 8"} - , - "arces/servers/ares/ercole/cpu/core-7/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore7" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 7" , - "label" : "Temperatura Server ERCOLE Core 7"} - , - "arces/servers/ares/ercole/cpu/core-6/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore6" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 6" , - "label" : "Temperatura Server ERCOLE Core 6"} - , - "arces/servers/ares/ercole/cpu/core-5/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore5" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 5" , - "label" : "Temperatura Server ERCOLE Core 5"} - , - "arces/servers/ares/ercole/cpu/core-4/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore4" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 4" , - "label" : "Temperatura Server ERCOLE Core 4"} - , - "arces/servers/ares/ercole/cpu/core-3/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore3" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 3" , - "label" : "Temperatura Server ERCOLE Core 3"} - , - "arces/servers/ares/ercole/cpu/core-2/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore2" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 2" , - "label" : "Temperatura Server ERCOLE Core 2"} - , - "arces/servers/ares/ercole/cpu/core-1/temperature" : { - "observation" : "arces-monitor:ServerErcoleCore1" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE Core 1" , - "label" : "Temperatura Server ERCOLE Core 1"} - , - "arces/servers/mars/mml/cpu/core-6/temperature" : { - "observation" : "arces-monitor:ServerMml6" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 6" , - "label" : "Temperatura Server MML Core 6"} - , - "arces/servers/mars/mml/cpu/core-5/temperature" : { - "observation" : "arces-monitor:ServerMml5" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 5" , - "label" : "Temperatura Server MML Core 5"} - , - "arces/servers/mars/mml/cpu/core-4/temperature" : { - "observation" : "arces-monitor:ServerMml4" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 4" , - "label" : "Temperatura Server MML Core 4"} - , - "arces/servers/mars/mml/cpu/core-3/temperature" : { - "observation" : "arces-monitor:ServerMml3" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 3" , - "label" : "Temperatura Server MML Core 3"} - , - "arces/servers/mars/mml/cpu/core-2/temperature" : { - "observation" : "arces-monitor:ServerMml2" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 2" , - "label" : "Temperatura Server MML Core 2"} - , - "arces/servers/mars/mml/cpu/core-1/temperature" : { - "observation" : "arces-monitor:ServerMml1" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML Core 1" , - "label" : "Temperatura Server MML Core 1"} - , - "arces/servers/mars/giove/cpu/core-6/temperature" : { - "observation" : "arces-monitor:ServerGiove6" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 6" , - "label" : "Temperatura Server GIOVE Core 6"} - , - "arces/servers/mars/giove/cpu/core-5/temperature" : { - "observation" : "arces-monitor:ServerGiove5" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 5" , - "label" : "Temperatura Server GIOVE Core 5"} - , - "arces/servers/mars/giove/cpu/core-4/temperature" : { - "observation" : "arces-monitor:ServerGiove4" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 4" , - "label" : "Temperatura Server GIOVE Core 4"} - , - "arces/servers/mars/giove/cpu/core-3/temperature" : { - "observation" : "arces-monitor:ServerGiove3" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 3" , - "label" : "Temperatura Server GIOVE Core 3"} - , - "arces/servers/mars/giove/cpu/core-2/temperature" : { - "observation" : "arces-monitor:ServerGiove2" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 2" , - "label" : "Temperatura Server GIOVE Core 2"} - , - "arces/servers/mars/giove/cpu/core-1/temperature" : { - "observation" : "arces-monitor:ServerGiove1" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE Core 1" , - "label" : "Temperatura Server GIOVE Core 1"} - , - "arces/servers/mars/mml/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerMmlHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML HDD SDA" , - "label" : "Temperatura Server MML HDD SDA"} - , - "arces/servers/mars/mml/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerMmlHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server MML HDD SDB" , - "label" : "Temperatura Server MML HDD SDB"} - , - "arces/servers/mars/giove/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDA" , - "label" : "Temperatura Server GIOVE HDD SDA"} - , - "arces/servers/mars/giove/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDB" , - "label" : "Temperatura Server GIOVE HDD SDB"} - , - "arces/servers/mars/giove/hd/sdc/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdc" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDC" , - "label" : "Temperatura Server GIOVE HDD SDC"} - , - "arces/servers/mars/giove/hd/sdd/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdd" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDD" , - "label" : "Temperatura Server GIOVE HDD SDD"} - , - "arces/servers/mars/giove/hd/sdf/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdf" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDF" , - "label" : "Temperatura Server GIOVE HDD SDF"} - , - "arces/servers/mars/giove/hd/sdg/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdg" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDG" , - "label" : "Temperatura Server GIOVE HDD SDG"} - , - "arces/servers/mars/giove/hd/sdh/temperature" : { - "observation" : "arces-monitor:ServerGioveHDDSdh" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server GIOVE HDD SDH" , - "label" : "Temperatura Server GIOVE HDD SDH"} - , - "arces/servers/mars/marsamba/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerSambaHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server SAMBA HDD SDA" , - "label" : "Temperatura Server SAMBA HDD SDA"} - , - "arces/servers/mars/marsamba/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerSambaHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura Server SAMBA HDD SDB" , - "label" : "Temperatura Server SAMBA HDD SDB"} - , - "arces/servers/ares/ercole/hd/sda/temperature" : { - "observation" : "arces-monitor:ServerErcoleHDDSda" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE HDD SDA" , - "label" : "Temperatura Server ERCOLE HDD SDA"} - , - "arces/servers/ares/ercole/hd/sdb/temperature" : { - "observation" : "arces-monitor:ServerErcoleHDDSdb" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Ares" , - "comment" : "Temperatura Server ERCOLE HDD SDB" , - "label" : "Temperatura Server ERCOLE HDD SDB"} - , - "5CCF7F15676D/temperature" : { - "observation" : "arces-monitor:5CCF7F15676D-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura zona rack sala server Toffano" , - "label" : "Temperatura zona rack"} - , - "5CCF7F15676D/humidity" : { - "observation" : "arces-monitor:5CCF7F15676D-humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Mars" , - "comment" : "Umidità zona rack sala server Toffano" , - "label" : "Umidità zona rack"} - , - "5CCF7F1B599E/temperature" : { - "observation" : "arces-monitor:5CCF7F1B599E-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura zona finestra sala server Toffano" , - "label" : "Temperatura zona finestra"} - , - "5CCF7F1B599E/humidity" : { - "observation" : "arces-monitor:5CCF7F1B599E-humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Mars" , - "comment" : "Umidità zona finestra sala server Toffano" , - "label" : "Umidità zona finestra"} - , - "5CCF7F151DC9/temperature" : { - "observation" : "arces-monitor:5CCF7F151DC9-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Mars" , - "comment" : "Temperatura esterna sala server Toffano" , - "label" : "Temperatura esterna"} - , - "5CCF7F1B58AC/temperature" : { - "observation" : "arces-monitor:5CCF7F1B58AC-temperature" , - "unit" : "qudt-unit-1-1:DegreeCelsius" , - "location" : "arces-monitor:Star" , - "comment" : "Temperatura zona rack sala server Pepoli" , - "label" : "Temperatura zona rack"} - , - "5CCF7F1B58AC/humidity" : { - "observation" : "arces-monitor:5CCF7F1B58AC-humidity" , - "unit" : "qudt-unit-1-1:Percent" , - "location" : "arces-monitor:Star" , - "comment" : "Umidità zona rack sala server Pepoli" , - "label" : "Umidità zona rack"} - } - } - , - "updates" : { - "ADD_OBSERVATION" : { - "sparql" : "DELETE {?observation ?p ?o . ?q ?p1 ?o1} INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity) . OPTIONAL {?observation rdf:type sosa:Observation ; ?p ?o ; sosa:hasResult ?q . ?q ?p1 ?o1 }}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - , - "comment" : { - "type" : "literal" , - "value" : ""} - , - "label" : { - "type" : "literal" , - "value" : ""} - , - "location" : { - "type" : "uri" , - "value" : ""} - , - "topic" : { - "type" : "literal" , - "value" : ""}, - "unit" : { - "type" : "uri" , - "value" : ""} - - } - } - , - "UPDATE_OBSERVATION_VALUE" : { - "sparql" : "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - , - "value" : { - "type" : "literal" , - "value" : ""} - } - } - } - , - "queries" : { - "OBSERVATIONS_TOPICS" : { - "sparql" : "SELECT ?observation ?topic WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}"} - , - "OBSERVATIONS_VALUES" : { - "sparql" : " SELECT ?observation ?location ?quantity ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}"} - , - "OBSERVATIONS_VALUES_BY_LOCATION" : { - "sparql" : " SELECT ?label ?value ?unit WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "location" : { - "type" : "uri" , - "value" : ""} - } - } - , - "OBSERVATIONS_VALUES_BY_UNIT" : { - "sparql" : " SELECT ?location ?label ?value WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "unit" : { - "type" : "uri" , - "value" : ""} - } - } - , - "OBSERVATION" : { - "sparql" : " SELECT ?location ?label ?quantity ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}" , - "forcedBindings" : { - "observation" : { - "type" : "uri" , - "value" : ""} - } - } - } -} diff --git a/tools/randomNumbers.jsap b/tools/randomNumbers.jsap deleted file mode 100644 index 60171975..00000000 --- a/tools/randomNumbers.jsap +++ /dev/null @@ -1,74 +0,0 @@ -{ - "parameters":{ - "host":"localhost", - "ports":{ - "http":8000, - "https":8443, - "ws":9000, - "wss":9443} - , - "paths":{ - "query":"/query", - "update":"/update", - "subscribe":"/subscribe", - "register":"/oauth/register", - "tokenRequest":"/oauth/token", - "securePath":"/secure"} - } - , - "namespaces":{ - "rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs":"http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rnd":"http://wot.arces.unibo.it/random#"} - , - "extended":{ - "sleep":100, - "gcnumbers":1000, - "generators":10} - , - "updates":{ - "RANDOM_NUMBER":{ - "sparql":"INSERT DATA {?number rdf:type rnd:RandomNumber . ?number rnd:hasValue ?value}", - "forcedBindings":{ - "number":{ - "type":"uri", - "value":""} - , - "value":{ - "type":"literal", - "value":""} - } - } - , - "UPDATE_MEAN":{ - "sparql":"DELETE {?mean rnd:hasValue ?oldValue ; rnd:hasCounter ?oldCounter} INSERT {?mean rdf:type rnd:Mean . ?mean rnd:hasValue ?value ; rnd:hasCounter ?counter} WHERE {OPTIONAL{?mean rdf:type rnd:Mean ; rnd:hasValue ?oldValue ; rnd:hasCounter ?oldCounter}}", - "forcedBindings":{ - "mean":{ - "type":"uri", - "value":""} - , - "value":{ - "type":"literal", - "value":""} - , - "counter":{ - "type":"literal", - "value":""} - } - } - , - "DELETE_NUMBERS":{ - "sparql":"DELETE {?number rdf:type rnd:RandomNumber . ?number rnd:hasValue ?value} WHERE {?number rdf:type rnd:RandomNumber . ?number rnd:hasValue ?value}"} - } - , - "queries":{ - "COUNT_NUMBERS":{ - "sparql":"SELECT (COUNT(?number) AS ?numbers) WHERE {?number rdf:type rnd:RandomNumber}"} - , - "RANDOM_NUMBER":{ - "sparql":"SELECT ?number ?value WHERE {?number rdf:type rnd:RandomNumber . ?number rnd:hasValue ?value}"} - , - "MEAN":{ - "sparql":"SELECT ?mean ?counter ?value WHERE {?mean rdf:type rnd:Mean . ?mean rnd:hasValue ?value . ?mean rnd:hasCounter ?counter}"} - } -} diff --git a/tools/sepa.jks b/tools/sepa.jks deleted file mode 100644 index 4b4d14d0ad13840ce7960e7ab898fe46f439e0a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2317 zcmd6o={wX58^>q0%-Gkc$&g+64bj9wwlpF8n&n{ZYi0;%TFlsGElt)ajU|b)B}&=F zi0n(Yk;qyiOB|i&d9LgH3D1lB#pk}iFYfDmeXsjISUFe$fk2R>0{>IY1ot2pU-$4M zb|`om_8kO*LjW@D0N`Lk$T5LoU|9$^7z_u&$grjPQpWeNp)+?%B7Tk08!AJ^+Hq_nwwu41kD{bxSZf**5y zn>%ua<_UeH{llWfw7Q!$6^E+5QQDRLC&mT7v$%A(I-1 zLQ-3yZkI9-6U|;Mq!JuIjOHztxX(Qz&GbGrohh(0-HY?#(lNll#b+d#4SuVpwMyvh zmRn=DoEsdry$x&L3QH%wEkGBoB-it696AWclkIsx{(HRHsiI*eNV^G!l@G}AH-Tu zex=n!$LH&xN7Z0?_C_r4P*tqLEEjB^raCzd( zPBOIBrCDp{IftlHXE$5qm(HAar;>$$Y+~IId#I>5$|t|vO+_bkWK(1?fV2F9IKNuz z=l<$}fs11)x|)-pSizvfbB0@75cx;RA?;bTCz)mN{T(i1EF$ba97x@S+0-`5Q(|Pk zY6Y3Nb>uu&clwyBxa^UA<0Anjs(BN?_F8wZAXZ_fU%es~mVaE?YqvDf49V12Tz5k! zr|S90_!284`qTQmgIk;|_d*#y{Q7I}Q^p_Mg{!CAqiKm>{?L^hy1Ge;l#Y&y z-13^FH|nC44s08Vj~OG`3IHP776q%U3ob5`VH328KmWK;VNh3%b!)*OG$(%yGp-eV z(JW@7@hJqI#cYM#!zt+%TJRs|h-w)FB2 z@Fbwk-3jh4!ERm{c|h*B0Lm+FVXAFzW`x!cbq^q-O@jm7+zEsuEn53X=1p{WBZdUK z11AO9F=qi)j50=91*5ELe}q^-6~O)%{|`4NgT?-B>(SSNlEIuHfDA@J$Y3yNO`hbD zJjrK}-o5}Ej? z?;MlMy_$OAU<>C^LlQbQ_xvl#DO`krY*t@?*-7199v^|a@YQNBO`F&l<*KRU7b;CQ zgoZ*W5|o;-@4>e1n|i1j-MWevNwi{N(s@mh7n*U2Z9*qbC;Z%Zo>IuO3dG+__D6<{ z>W^Pidoa46Bia~k`Lf%QEV8;0{uFzSLhZ+y-B<&tgHHw8EYq8_p3awc)=Qe!_y3@s zrk5$ypK-Wx`z+_u``fi%HiMz60#Mu0NgZtDc!fCw0YB|uq`0=1>g$4~K%ig{_>m~^ zpQNBTU;;3{q5dueKggIB4~YoV7!%@~pIIONw~0sDJTegslB6vI!#?!&xNmy!H153S z>ynAdlEbpOvs9S=%5tqZ?8R`;T;0cs@rW-Ijjyqmq$+imsU7TW+oAyfEh2EtdQWO2 ztjeG9Q0nsH$)=U29oxuz?2jaHbD3?G_|Wwm3mpr`d@JI5X2Y*gt$LK4Do6N7BF=RS zPc3iv$jzO;4qEg3$?O?ut3EYJPqU&is)~?pPQ!I%*xieoA;*Xz_{p}FB=ak)L!N2- zi7NvcP1CV!#*el2y<5JuV`h`H@VIs!CB=zT(SmYPQbC3g89Hg=La&mLkfH=>Oi1ck zHr8}+igV5C*!78FGZm+el_AD7-4dbFa80vAG-Z>%S!3j+Qx threads = new ArrayList(); - - class HeapBugTestConsumer extends Consumer { - private int number; - private long notifications; - - public HeapBugTestConsumer(ApplicationProfile appProfile, String subscribeID,int number) throws SEPAProtocolException, SEPASecurityException { - super(appProfile, subscribeID); - this.number = number; - notifications = 0; - } - - @Override - public void onResults(ARBindingsResults results) { - notifications++; - System.out.println("Consumer #"+number+" Notifications:"+notifications); - - } - - @Override - public void onAddedResults(BindingsResults results) { - - } - - @Override - public void onRemovedResults(BindingsResults results) { - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - - } - - class HeapBugTestProducer extends Thread { - private Producer producer; - private int index; - private long number; - - public HeapBugTestProducer(int index) - throws SEPAProtocolException, SEPASecurityException { - this.index = index; - producer = new Producer(app, "UPDATE_OBSERVATION_VALUE"); - number = 0; - } - - public void run() { - Bindings fb = new Bindings(); - fb.addBinding("observation", new RDFTermURI(observation + index)); - while (true) { - int n = (int) (MIN_SLEEP + DELTA_MAX_SLEEP * Math.random()); - try { - Thread.sleep(n); - } catch (InterruptedException e) { - return; - } - fb.addBinding("value", new RDFTermLiteral(String.format("%d", number++))); - producer.update(fb); - } - } - } - - public static void printUsage() { - System.out.println("Usage: HeapBugTest [COMMANDS][OPTIONS]"); - System.out.println("COMMANDS:"); - System.out.println("-h to print this guide"); - System.out.println("-pro (number of producers, default: 10)"); - System.out.println("-con (number of consumers, default: 10)"); - System.out.println("-min (minimun sleep time of producers, default 500)"); - System.out.println("-max (delta sleep time of producers, default 500)"); - System.exit(1); - } - - public static void main(String[] args) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException { - if (args.length > 1) { - for (int index = 0; index < args.length; index++) { - switch (args[index]) { - case "-pro": - index++; - if (index < args.length) try{ - N_OBSERVATIONS = Integer.parseInt(args[index]); - - }catch(NumberFormatException e) { - printUsage(); - } - else printUsage(); - break; - case "-con": - index++; - if (index < args.length) try{ - N_CONSUMERS = Integer.parseInt(args[index]); - - }catch(NumberFormatException e) { - printUsage(); - }else printUsage(); - break; - case "-min": - index++; - if (index <= args.length) try{ - MIN_SLEEP = Integer.parseInt(args[index]); - - }catch(NumberFormatException e) { - printUsage(); - }else printUsage(); - break; - case "-max": - index++; - if (index <= args.length) try{ - DELTA_MAX_SLEEP = Integer.parseInt(args[index]); - }catch(NumberFormatException e) { - printUsage(); - }else printUsage(); - break; - } - } - } else if (args.length == 1) { - if (!args[0].equals("-default")) - printUsage(); - } else - printUsage(); - - app = new ApplicationProfile("mqttMonitoring.jsap"); - - System.out.println("Context creation"); - creator = new Producer(app, "ADD_OBSERVATION"); - - System.out.println("Consumers creation"); - for (int i=0; i < N_CONSUMERS;i++) { - HeapBugTestConsumer consumer = new HeapBugTest().new HeapBugTestConsumer(app,"OBSERVATIONS",i); - consumer.subscribe(null); - } - - Bindings fb = new Bindings(); - fb.addBinding("unit", new RDFTermURI(unit)); - - System.out.println("Producers creation"); - for (int i = 0; i < N_OBSERVATIONS; i++) { - fb.addBinding("observation", new RDFTermURI(observation + i)); - fb.addBinding("comment", new RDFTermLiteral(comment + i)); - fb.addBinding("label", new RDFTermLiteral(label + i)); - fb.addBinding("location", new RDFTermURI(location + i)); - creator.update(fb); - HeapBugTestProducer th = new HeapBugTest().new HeapBugTestProducer(i); - threads.add(th); - th.start(); - } - - System.out.println("Press any key to exit..."); - System.in.read(); - - for (HeapBugTestProducer th : threads) - th.interrupt(); - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/LCDProducer.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/LCDProducer.java deleted file mode 100644 index b17e9d69..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/LCDProducer.java +++ /dev/null @@ -1,95 +0,0 @@ -package it.unibo.arces.wot.sepa.apps; - -import java.util.HashMap; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class LCDProducer extends Producer { - - public LCDProducer(ApplicationProfile appProfile, String updateID) - throws SEPAProtocolException, SEPASecurityException { - super(appProfile, updateID); - } - - private static void justText(Producer client) { - Bindings bind = new Bindings(); - while (true) { - bind = new Bindings(); - bind.addBinding("value", new RDFTermLiteral("Let Things Talk")); - client.update(bind); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - bind = new Bindings(); - bind.addBinding("value", new RDFTermLiteral("Vaimee!")); - client.update(bind); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - private static void superCar(Producer client) { - Bindings bind = new Bindings(); - - HashMap graphics = new HashMap(); - graphics.put(0, "* *"); - graphics.put(1, "** **"); - graphics.put(2, "*** ***"); - graphics.put(3, "**** ****"); - graphics.put(4, "***** *****"); - graphics.put(5, "****** ******"); - graphics.put(6, "******* *******"); - graphics.put(7, "****************"); - - while (true) { - for (int i = 0; i < 8; i++) { - bind = new Bindings(); - bind.addBinding("value", new RDFTermLiteral(graphics.get(i))); - client.update(bind); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - for (int i = 7; i > 0; i--) { - bind = new Bindings(); - bind.addBinding("value", new RDFTermLiteral(graphics.get(i))); - client.update(bind); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - } - - public static void main(String[] args) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException{ - - ApplicationProfile appProfile = new ApplicationProfile("wot-plugfest.jsap"); - - LCDProducer client = new LCDProducer(appProfile, "LCD"); - - superCar(client); - justText(client); - - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/RealTimeIoTResourceUpdate.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/RealTimeIoTResourceUpdate.java deleted file mode 100644 index 66b9105e..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/RealTimeIoTResourceUpdate.java +++ /dev/null @@ -1,73 +0,0 @@ -package it.unibo.arces.wot.sepa.apps; - -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; - -public class RealTimeIoTResourceUpdate { - protected static final Logger logger = LogManager.getLogger("RealTimeIoTResourceUpdate"); - - private static int nThreads = 1; - private static int nUpdates = 1000; - - private static ExecutorService producers = Executors.newFixedThreadPool(nThreads); - - public class ProducerThread extends Producer implements Runnable { - - public ProducerThread(ApplicationProfile appProfile, String updateID) throws SEPAProtocolException, SEPASecurityException{ - super(appProfile, updateID); - } - - @Override - public void run() { - int i = 0; - Bindings bindings = new Bindings(); - bindings.addBinding("resource", new RDFTermURI("iot:Resource_"+UUID.randomUUID().toString())); - while (i++ < nUpdates) { - double value = Math.random()*100; - bindings.addBinding("value", new RDFTermLiteral(String.format("%.2f", value))); - update(bindings); - } - } - } - - public static void main(String[] args) throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException { - ApplicationProfile app = null; - - app = new ApplicationProfile("sapexamples/GatewayProfile.jsap"); - - - for (int i=0; i < nThreads; i++) { - producers.execute(new RealTimeIoTResourceUpdate().new ProducerThread(app,"UPDATE_RESOURCE")); - - } - /* - synchronized(th){ - try { - th.wait(); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - }*/ - - try { - producers.awaitTermination(60, TimeUnit.SECONDS); - } catch (InterruptedException e) { - logger.warn(e.getMessage()); - } - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java deleted file mode 100644 index 198a968b..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java +++ /dev/null @@ -1,74 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public class BasicClient extends ChatClient { - private static final Logger logger = LogManager.getLogger(); - - private String user; - private Users users; - private int messages = 10; - private int notifications = 0; - private int expectedNotifications = 0; - - public BasicClient(String userURI, Users users, int messages) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(userURI); - this.user = userURI; - this.users = users; - this.messages = messages; - if (users.getUsers().size() > 1) - this.expectedNotifications = messages * (users.getUsers().size()-1); - else - this.expectedNotifications = messages; - } - - @Override - public void run() { - while (!joinChat()) { - try { - logger.info("Joining the chat..."); - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - } - logger.info("Chat joined!"); - - for (int i = 0; i < messages; i++) { - for (String receiver : users.getUsers()) { - if (receiver.equals(user) && users.getUsers().size() > 1) - continue; - sendMessage(receiver, "MSG #" + i); - } - } - - try { - synchronized (this) { - wait(); - } - } catch (InterruptedException e) { - } - } - - @Override - public void onMessage(String from, String message) { - - } - - @Override - public void onMessageRemoved(String messageURI) { - notifications++; - if (notifications == expectedNotifications) { - synchronized(this) { - notify(); - } - } - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java deleted file mode 100644 index feb4024e..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; - -public class BasicHandler implements ISubscriptionHandler { - private static final Logger logger = LogManager.getLogger(); - - @Override - public void onSemanticEvent(Notification notify) { - logger.info("Notify: "+notify); - } - - @Override - public void onBrokenSocket() { - logger.info("Broken socket"); - - } - - @Override - public void onError(ErrorResponse errorResponse) { - logger.info("Error: "+errorResponse); - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java deleted file mode 100644 index 81bbcfc1..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java +++ /dev/null @@ -1,39 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public abstract class ChatClient implements Runnable { - - protected Sender sender; - private Receiver receiver; - private Remover remover; - protected String userURI; - - public ChatClient(String userURI) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - this.userURI = userURI; - sender = new Sender(userURI); - receiver = new Receiver(userURI,this); - remover = new Remover(userURI,this); - } - - public boolean joinChat() { - if (!remover.joinChat()) return false; - if (!receiver.joinChat()) return false; - return true; - } - - public boolean leaveChat() { - if (!remover.leaveChat()) return false; - if (!receiver.leaveChat()) return false; - return true; - } - - public boolean sendMessage(String receiverURI,String message) { - return sender.sendMessage(receiverURI,message); - } - - public abstract void onMessage(String from,String message); - public abstract void onMessageRemoved(String messageURI); -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/DeleteAll.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/DeleteAll.java deleted file mode 100644 index 8a89b17a..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/DeleteAll.java +++ /dev/null @@ -1,18 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class DeleteAll extends Producer { - - public DeleteAll() throws SEPAProtocolException, SEPAPropertiesException { - super(new ApplicationProfile("chat.jsap"), "DELETE_ALL"); - } - - public void clean() { - update(null); - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Message.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Message.java deleted file mode 100644 index 82d155cd..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Message.java +++ /dev/null @@ -1,49 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -public class Message { - private String from; - private String to; - private String time; - private String text; - private String msg; - - /** - * Represent a chat message - * - * */ - public Message(String msg, String from,String to,String text,String time) { - this.msg = msg; - this.from = from; - this.to= to; - this.text = text; - this.time = time; - } - - public String getMessage() { - return msg; - - } - public String getTo() { - return to; - } - - public String getTime() { - return time; - } - - public String getText() { - return text; - } - - public String toString() { - return "From: <"+from+"> To: <"+to+"> Message: <"+text+">"; - } - - public String toCSV() { - return msg+ " " +from + " "+ to + " " + text+ " "+ time; - } - - public String getFrom() { - return from; - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java deleted file mode 100644 index 9370e3b8..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/PingPongClient.java +++ /dev/null @@ -1,21 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public class PingPongClient extends BasicClient { - private int index = 0; - - public PingPongClient(String userURI, Users users) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(userURI, users,1); - } - - @Override - public void onMessage(String from,String message) { - super.onMessage(from,message); - sendMessage(from, "Reply #" + index++); - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java deleted file mode 100644 index 7fe6e797..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java +++ /dev/null @@ -1,106 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.Aggregator; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class Receiver extends Aggregator { - private static final Logger logger = LogManager.getLogger(); - - private Bindings message = new Bindings(); - private boolean joined = false; - - private ChatClient client; - - public Receiver(String receiverURI,ChatClient client) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("chat.jsap"), "SENT", "SET_RECEIVED"); - - message.addBinding("receiver", new RDFTermURI(receiverURI)); - - this.client = client; - } - - public boolean joinChat() { - if (joined) - return true; - - Response ret = subscribe(message); - joined = !ret.isError(); - - if (joined) - onAddedResults(((SubscribeResponse) ret).getBindingsResults()); - - return joined; - } - - public boolean leaveChat() { - if (!joined) - return true; - - Response ret = unsubscribe(); - joined = !ret.isUnsubscribeResponse(); - - return !joined; - } - - @Override - public void onAddedResults(BindingsResults results) { - // Variables: ?message ?sender ?name ?text ?time - for (Bindings bindings : results.getBindings()) { - logger.info("SENT "+bindings.getBindingValue("message")); - client.onMessage(bindings.getBindingValue("sender"), bindings.getBindingValue("text")); - - // Set received - Bindings setReceived = new Bindings(); - setReceived.addBinding("message", new RDFTermURI(bindings.getBindingValue("message"))); - - - update(setReceived); - - } - } - - @Override - public void onResults(ARBindingsResults results) { - } - - @Override - public void onRemovedResults(BindingsResults results) { - // Variables: ?message ?sender ?name ?text ?time - for (Bindings bindings : results.getBindings()) { - logger.info("REMOVED "+bindings.getBindingValue("message")); - - client.onMessageRemoved(bindings.getBindingValue("message")); - } - } - - @Override - public void onBrokenSocket() { - joined = false; - - while (!joinChat()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - } - } - - @Override - public void onError(ErrorResponse errorResponse) { - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java deleted file mode 100644 index d97edfc7..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java +++ /dev/null @@ -1,92 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.Aggregator; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class Remover extends Aggregator { - private static final Logger logger = LogManager.getLogger(); - - private Bindings sender = new Bindings(); - private boolean joined = false; - - public Remover(String senderURI,ChatClient client) throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { - super(new ApplicationProfile("chat.jsap"), "RECEIVED", "REMOVE"); - - sender.addBinding("sender", new RDFTermURI(senderURI)); - } - - public boolean joinChat() { - if (joined) - return true; - - Response ret = subscribe(sender); - joined = !ret.isError(); - - if (joined) - onAddedResults(((SubscribeResponse) ret).getBindingsResults()); - - return joined; - } - - public boolean leaveChat() { - if (!joined) - return true; - - Response ret = unsubscribe(); - joined = !ret.isUnsubscribeResponse(); - - return !joined; - } - - @Override - public void onResults(ARBindingsResults results) { - } - - @Override - public void onAddedResults(BindingsResults results) { - for (Bindings bindings : results.getBindings()) { - logger.info("RECEIVED From: "+bindings.getBindingValue("message")); - - // Variables: ?message ?time - update(bindings); - } - - } - - @Override - public void onRemovedResults(BindingsResults results) { - - } - - @Override - public void onBrokenSocket() { - joined = false; - - while (!joinChat()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - } - } - - @Override - public void onError(ErrorResponse errorResponse) { - - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java deleted file mode 100644 index 42d291c6..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class SEPAChatTest { - private static final Logger logger = LogManager.getLogger(); - - private int N_CLIENTS = 2; - private int BASE = 0; - private int MESSAGES = 10; - private Users users; - private static List clients = new ArrayList(); - - private enum CLIENT_TYPE { - PING_PONG, BASIC - }; - - private CLIENT_TYPE type = CLIENT_TYPE.BASIC; - - public SEPAChatTest() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { - try { - ApplicationProfile app = new ApplicationProfile("chat.jsap"); - BASE = app.getExtendedData().get("base").getAsInt(); - N_CLIENTS = app.getExtendedData().get("clients").getAsInt(); - String sType = app.getExtendedData().get("type").getAsString(); - switch(sType.toUpperCase()) { - case "BASIC": - type = CLIENT_TYPE.BASIC; - MESSAGES = app.getExtendedData().get("messages").getAsInt(); - break; - case "PINGPONG": - type = CLIENT_TYPE.PING_PONG; - break; - } - } catch (Exception e) { - logger.error(e.getMessage()); - } - - new DeleteAll().clean(); - - // Register chat BOTS - UserRegistration registration = new UserRegistration(); - for (int i = BASE; i < BASE + N_CLIENTS; i++) { - registration.register("ChatBot" + i); - } - - users = new Users(); - } - - public boolean start() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - if (!users.joinChat()) - return false; - - for (String user : users.getUsers()) { - ChatClient client = null; - switch (type) { - case BASIC: - client = new BasicClient(user, users,MESSAGES); - break; - case PING_PONG: - client = new PingPongClient(user, users); - break; - default: - client = new BasicClient(user, users,MESSAGES); - } - - Thread th = new Thread(client); - clients.add(th); - th.start(); - } - - return true; - } - - public static void main(String[] args) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException, InterruptedException { - - SEPAChatTest chat = new SEPAChatTest(); - - try { - if (!chat.start()) - System.exit(-1); - } catch (Exception e) { - logger.error(e.getMessage()); - System.exit(-1); - } - - for (Thread th:clients) th.join(60000); - - System.exit(0); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java deleted file mode 100644 index 5cf4948f..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Sender.java +++ /dev/null @@ -1,42 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; -import it.unibo.arces.wot.sepa.timing.Timings; - -public class Sender extends Producer { - private static final Logger logger = LogManager.getLogger(); - - private Bindings message = new Bindings(); - - public Sender(String senderURI) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("chat.jsap"), "SEND"); - - message.addBinding("sender", new RDFTermURI(senderURI)); - } - - public boolean sendMessage(String receiverURI,String text) { - logger.info("SEND From: "+message.getBindingValue("sender")+" To: "+receiverURI+" "+message); - - message.addBinding("receiver", new RDFTermURI(receiverURI)); - message.addBinding("text", new RDFTermLiteral(text)); - - long start = Timings.getTime(); - boolean ret = update(message).isUpdateResponse(); - long stop = Timings.getTime(); - String msg = message.getBindingValue("sender")+message.getBindingValue("receiver")+message.getBindingValue("text"); - Timings.log(msg.replace(" ", "_"), start, stop); - return ret; - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java deleted file mode 100644 index e57c257e..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java +++ /dev/null @@ -1,207 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.TimeZone; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.GenericClient; - -public class UpdateQueryTest extends GenericClient { - private static final Logger logger = LogManager.getLogger(); - - private String SEND; - private String SET_RECEIVED; - private String REMOVE; - private String DELETE_ALL; - private String REGISTER_USER; - - private String SENT; - private String RECEIVED; - private String USERS; - - private int clients; - - public UpdateQueryTest(ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("chat.jsap"), handler); - - SEND = appProfile.getSPARQLUpdate("SEND"); - SET_RECEIVED = appProfile.getSPARQLUpdate("SET_RECEIVED"); - REMOVE = appProfile.getSPARQLUpdate("REMOVE"); - DELETE_ALL = appProfile.getSPARQLUpdate("DELETE_ALL"); - REGISTER_USER = appProfile.getSPARQLUpdate("REGISTER_USER"); - - SENT = appProfile.getSPARQLQuery("SENT"); - RECEIVED = appProfile.getSPARQLQuery("RECEIVED"); - USERS = appProfile.getSPARQLQuery("USERS"); - - clients = appProfile.getExtendedData().get("clients").getAsInt(); - - //Logging - TimeZone tz = TimeZone.getTimeZone("UTC"); - DateFormat df = new SimpleDateFormat("yyyyMMdd_HH_mm_ss"); // Quoted "Z" to indicate UTC, no timezone offset - df.setTimeZone(tz); - String nowAsISO = df.format(new Date()); - System.setProperty("logFilename", nowAsISO); - org.apache.logging.log4j.core.LoggerContext ctx = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false); - ctx.reconfigure(); - } - - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - BasicHandler handler = new BasicHandler(); - UpdateQueryTest test = new UpdateQueryTest(handler); - - test.run(); - } - - public void run() { - deleteAll(); - - for (int i = 0; i < clients; i++) { - registerUser("User"+i); - } - - List users = users(); - - // 1 - SEND - for (String receiver : users) send(users.get(0),receiver,"Message"); - - // 2 - SENT - List messages = new ArrayList(); - for (String receiver : users) { - List msg = sent(receiver); - messages.addAll(msg); - } - - // 3 - SET RECEIVED - for(String message : messages) setReceived(message); - - // 4 - RECEIVED - messages.clear(); - List msg = received(users.get(0)); - messages.addAll(msg); - - // 5 - REMOVE - for(String message : messages) remove(message); - } - - public void send(String sender, String receiver, String text) { - Bindings bindings = new Bindings(); - bindings.addBinding("sender", new RDFTermURI(sender)); - bindings.addBinding("receiver", new RDFTermURI(receiver)); - bindings.addBinding("text", new RDFTermLiteral(text)); - - long start = new Date().toInstant().toEpochMilli(); - update(SEND, bindings); - long stop = new Date().toInstant().toEpochMilli(); - - logger.info("SEND "+(stop-start)); - } - - public void setReceived(String message) { - Bindings bindings = new Bindings(); - bindings.addBinding("message", new RDFTermURI(message)); - - long start = new Date().toInstant().toEpochMilli(); - update(SET_RECEIVED, bindings); - long stop = new Date().toInstant().toEpochMilli(); - - logger.info("SET_RECEIVED "+(stop-start)); - } - - public void remove(String message) { - Bindings bindings = new Bindings(); - bindings.addBinding("message", new RDFTermURI(message)); - - long start = new Date().toInstant().toEpochMilli(); - update(REMOVE, bindings); - long stop = new Date().toInstant().toEpochMilli(); - - logger.info("REMOVE "+(stop-start)); - } - - public void deleteAll() { - update(DELETE_ALL, null); - } - - public boolean registerUser(String userName) { - Bindings bindings = new Bindings(); - bindings.addBinding("userName", new RDFTermLiteral(userName)); - - return this.update(REGISTER_USER, bindings).isUpdateResponse(); - } - - public List sent(String receiver) { - Bindings bindings = new Bindings(); - bindings.addBinding("receiver", new RDFTermURI(receiver)); - - ArrayList list = new ArrayList(); - - - long start = new Date().toInstant().toEpochMilli(); - Response ret = this.query(SENT, bindings); - long stop = new Date().toInstant().toEpochMilli(); - - logger.info("SENT "+(stop-start)); - - if (ret.isError()) return list; - - QueryResponse results = (QueryResponse) ret; - for (Bindings result : results.getBindingsResults().getBindings()) { - list.add(result.getBindingValue("message")); - } - - return list; - } - - public List received(String sender) { - Bindings bindings = new Bindings(); - bindings.addBinding("sender", new RDFTermURI(sender)); - - ArrayList list = new ArrayList(); - - long start = new Date().toInstant().toEpochMilli(); - Response ret = this.query(RECEIVED, bindings); - long stop = new Date().toInstant().toEpochMilli(); - - logger.info("RECEIVED "+(stop-start)); - - if (ret.isError()) return list; - - QueryResponse results = (QueryResponse) ret; - for (Bindings result : results.getBindingsResults().getBindings()) { - list.add(result.getBindingValue("message")); - } - - return list; - } - - public List users() { - Response ret = this.query(USERS, null); - ArrayList list = new ArrayList(); - - if (ret.isError()) return list; - - QueryResponse results = (QueryResponse) ret; - for (Bindings bindings : results.getBindingsResults().getBindings()) { - list.add(bindings.getBindingValue("user")); - } - - return list; - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UserRegistration.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UserRegistration.java deleted file mode 100644 index fdb084a1..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UserRegistration.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class UserRegistration extends Producer { - - public UserRegistration() throws SEPAProtocolException, SEPAPropertiesException { - super(new ApplicationProfile("chat.jsap"), "REGISTER_USER"); - } - - public void register(String userName) { - Bindings bindings = new Bindings(); - bindings.addBinding("userName", new RDFTermLiteral(userName)); - update(bindings); - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java deleted file mode 100644 index d23c10f3..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java +++ /dev/null @@ -1,102 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import java.util.HashMap; -import java.util.Set; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; - -public class Users extends Consumer { - private HashMap usersList = new HashMap(); - private boolean joined = false; - - public Users() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { - super(new ApplicationProfile("chat.jsap"), "USERS"); - } - - public boolean joinChat() { - if (joined) - return true; - - Response ret = subscribe(null); - joined = !ret.isError(); - - if (joined) - onAddedResults(((SubscribeResponse) ret).getBindingsResults()); - - return joined; - } - - public boolean leaveChat() { - if (!joined) - return true; - - Response ret = unsubscribe(); - joined = !ret.isUnsubscribeResponse(); - - return !joined; - } - - public Set getUsers() { - synchronized (usersList) { - return usersList.keySet(); - } - } - - public String getUserName(String user) { - synchronized (usersList) { - return usersList.get(user); - } - } - - @Override - public void onResults(ARBindingsResults results) { - } - - @Override - public void onAddedResults(BindingsResults results) { - synchronized (usersList) { - for (Bindings bindings : results.getBindings()) { - usersList.put(bindings.getBindingValue("user"), bindings.getBindingValue("userName")); - } - } - - } - - @Override - public void onRemovedResults(BindingsResults results) { - synchronized (usersList) { - for (Bindings bindings : results.getBindings()) { - usersList.remove(bindings.getBindingValue("user")); - } - } - } - - @Override - public void onBrokenSocket() { - joined = false; - - while (!joinChat()) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - } - } - - @Override - public void onError(ErrorResponse errorResponse) { - - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java deleted file mode 100644 index 6953b844..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java +++ /dev/null @@ -1,207 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.mqtt; - -import java.io.IOException; - -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttClient; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SSLSecurityManager; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class MQTTAdapter extends Producer implements MqttCallback { - private static final Logger logger = LogManager.getLogger(); - - private MqttClient mqttClient; - private String[] topicsFilter = null; - private String serverURI = null; - - public static void main(String[] args) throws IOException, SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - MQTTAdapter adapter = new MQTTAdapter(); - adapter.start(); - - System.out.println("Press any key to exit..."); - System.in.read(); - - adapter.stop(); - } - - public MQTTAdapter() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("mqtt.jsap"), "MQTT_MESSAGE"); - } - - @Override - public void connectionLost(Throwable arg0) { - logger.error("Connection lost: " + arg0.getMessage()); - - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - - } - - logger.warn("Connecting..."); - try { - mqttClient.connect(); - } catch (MqttException e) { - logger.fatal("Failed to connect: " + e.getMessage()); - continue; - } - - logger.warn("Subscribing..."); - try { - mqttClient.subscribe(topicsFilter); - } catch (MqttException e) { - logger.fatal("Failed to subscribe " + e.getMessage()); - continue; - } - - break; - } - - logger.info("Connected and subscribed!"); - - } - - @Override - public void deliveryComplete(IMqttDeliveryToken arg0) { - - } - - @Override - public void messageArrived(String topic, MqttMessage value) throws Exception { - logger.info(topic + " " + value.toString()); - - Bindings bindings = new Bindings(); - bindings.addBinding("topic", new RDFTermLiteral(topic)); - bindings.addBinding("value", new RDFTermLiteral(value.toString())); - bindings.addBinding("broker", new RDFTermLiteral(serverURI)); - update(bindings); - } - - public boolean start() { - /* - * test.mosquitto.org 1883 - * giove.arces.unibo.it 52877 - * - * */ - // MQTT - JsonObject mqtt = getApplicationProfile().getExtendedData().get("mqtt").getAsJsonObject(); - - String url = mqtt.get("url").getAsString(); - int port = mqtt.get("port").getAsInt(); - JsonArray topics = mqtt.get("topics").getAsJsonArray(); - - topicsFilter = new String[topics.size()]; - int i = 0; - for (JsonElement topic : topics) { - topicsFilter[i] = topic.getAsString(); - i++; - } - - boolean sslEnabled = false; - if (mqtt.get("ssl") != null) - sslEnabled = mqtt.get("ssl").getAsBoolean(); - - if (sslEnabled) { - serverURI = "ssl://" + url + ":" + String.format("%d", port); - } else { - serverURI = "tcp://" + url + ":" + String.format("%d", port); - } - - // Create client - logger.info("Creating MQTT client..."); - String clientID = MqttClient.generateClientId(); - logger.info("Client ID: " + clientID); - logger.info("Server URI: " + serverURI); - try { - mqttClient = new MqttClient(serverURI, clientID); - } catch (MqttException e) { - logger.error(e.getMessage()); - return false; - } - - // Connect - logger.info("Connecting..."); - MqttConnectOptions options = new MqttConnectOptions(); - if (sslEnabled) { - SSLSecurityManager sm; - try { - sm = new SSLSecurityManager("TLSv1","sepa.jks", "sepa2017", "sepa2017"); - } catch (UnrecoverableKeyException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException - | CertificateException | IOException e) { - logger.error(e.getMessage()); - return false; - } - logger.info("Set SSL security"); - try { - options.setSocketFactory(sm.getSSLContext().getSocketFactory()); - } catch (KeyManagementException | NoSuchAlgorithmException e) { - logger.error(e.getMessage()); - return false; - } - } - try { - mqttClient.connect(options); - } catch (MqttException e) { - logger.error(e.getMessage()); - } - - // Subscribe - mqttClient.setCallback(this); - logger.info("Subscribing..."); - try { - mqttClient.subscribe(topicsFilter); - } catch (MqttException e) { - logger.error(e.getMessage()); - return false; - } - - String printTopics = "Topic filter "; - for (String s : topicsFilter) { - printTopics += s + " "; - } - logger.info("MQTT client " + clientID + " subscribed to " + serverURI + printTopics); - - return true; - } - - public void stop() { - try { - if (topicsFilter != null) - mqttClient.unsubscribe(topicsFilter); - } catch (MqttException e1) { - logger.error("Failed to unsubscribe " + e1.getMessage()); - } - - try { - mqttClient.disconnect(); - } catch (MqttException e) { - logger.error("Failed to disconnect " + e.getMessage()); - } - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java deleted file mode 100644 index 075f6c61..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java +++ /dev/null @@ -1,56 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.mqtt; - -import java.util.Map.Entry; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class MQTTInitializer extends Producer { - private static final Logger logger = LogManager.getLogger("MQTTInitializer"); - - public MQTTInitializer() throws SEPAProtocolException, SEPAPropertiesException { - super(new ApplicationProfile("mqtt.jsap"), "ADD_OBSERVATION"); - } - - public void init() { - logger.info("Parse semantic mappings"); - JsonObject mappings = getApplicationProfile().getExtendedData().get("semantic-mappings") - .getAsJsonObject(); - - logger.info("Add observations"); - for (Entry mapping : mappings.entrySet()) addObservation(mapping); - } - - private void addObservation(Entry mapping) { - String topic = mapping.getKey(); - - String observation = mapping.getValue().getAsJsonObject().get("observation").getAsString(); - String unit = mapping.getValue().getAsJsonObject().get("unit").getAsString(); - String location = mapping.getValue().getAsJsonObject().get("location").getAsString(); - String comment = mapping.getValue().getAsJsonObject().get("comment").getAsString(); - String label = mapping.getValue().getAsJsonObject().get("label").getAsString(); - - Bindings bindings = new Bindings(); - bindings.addBinding("observation", new RDFTermURI(observation)); - bindings.addBinding("comment", new RDFTermLiteral(comment)); - bindings.addBinding("label", new RDFTermLiteral(label)); - bindings.addBinding("location", new RDFTermURI(location)); - bindings.addBinding("unit", new RDFTermURI(unit)); - bindings.addBinding("topic", new RDFTermLiteral(topic)); - - logger.info("Add observation: " + bindings); - update(bindings); - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java deleted file mode 100644 index 80ae6b07..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java +++ /dev/null @@ -1,49 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.mqtt; - -import java.io.IOException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public class MQTTMonitor { - private static final Logger logger = LogManager.getLogger("MQTTMonitor"); - - // Produce observations coming from MQTT matching with the semantic mapping - private static MQTTSmartifier smartifier; - - // Add observation based on the semantic mapping stored in JSAP - private static MQTTInitializer mqttInitializer; - - public static void main(String[] args) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - - logger.info("Create initializer"); - mqttInitializer = new MQTTInitializer(); - mqttInitializer.init(); - - // Create MQTT smartifier - logger.info("Create MQTT smartifier"); - smartifier = new MQTTSmartifier(); - - logger.info("Start MQTT smartifier"); - if (smartifier.start()) { - logger.info("Press any key to exit..."); - try { - System.in.read(); - } catch (IOException e) { - logger.warn(e.getMessage()); - } - - logger.info("Stop MQTT smartifier"); - smartifier.stop(); - - logger.info("Stopped"); - } - - System.exit(1); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java deleted file mode 100644 index a2ff2f17..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java +++ /dev/null @@ -1,392 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.mqtt; - -import java.io.IOException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; -import org.eclipse.paho.client.mqttv3.MqttCallback; -import org.eclipse.paho.client.mqttv3.MqttClient; -import org.eclipse.paho.client.mqttv3.MqttConnectOptions; -import org.eclipse.paho.client.mqttv3.MqttException; -import org.eclipse.paho.client.mqttv3.MqttMessage; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SSLSecurityManager; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.Aggregator; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class MQTTSmartifier extends Aggregator implements MqttCallback { - private static final Logger logger = LogManager.getLogger("MQTTSmartifier"); - - private MqttClient mqttClient; - - private String[] topicsFilter = { "#" }; - - // Topics mapping - private HashMap topic2observation = new HashMap(); - - public MQTTSmartifier() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("mqtt.jsap"), "OBSERVATIONS_TOPICS", "UPDATE_OBSERVATION_VALUE"); - } - - @Override - public void onAddedResults(BindingsResults results) { - for (Bindings bindings : results.getBindings()) { - topic2observation.put(bindings.getBindingValue("topic"), bindings.getBindingValue("observation")); - } - } - - private void updateObservationValue(String observation, String value) { - Bindings bindings = new Bindings(); - bindings.addBinding("observation", new RDFTermURI(observation)); - bindings.addBinding("value", new RDFTermLiteral(value)); - - logger.info("Update observation: " + bindings); - update(bindings); - } - - @Override - public void connectionLost(Throwable arg0) { - logger.error("Connection lost: " + arg0.getMessage()); - - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - - } - - logger.warn("Connecting..."); - try { - mqttClient.connect(); - } catch (MqttException e) { - logger.fatal("Failed to connect: " + e.getMessage()); - continue; - } - - logger.warn("Subscribing..."); - try { - mqttClient.subscribe(topicsFilter); - } catch (MqttException e) { - logger.fatal("Failed to subscribe " + e.getMessage()); - continue; - } - - break; - } - - logger.info("Connected and subscribed!"); - - } - - @Override - public void deliveryComplete(IMqttDeliveryToken arg0) { - - } - - @Override - public void messageArrived(String topic, MqttMessage value) throws Exception { - byte[] payload = value.getPayload(); - String converted = ""; - for (int i =0; i < payload.length; i++) { - if (payload[i] == 0) break; - converted += String.format("%c",payload[i]); - } - - mqttMessage(topic,converted); - } - - private void mqttMessage(String topic, String value) throws Exception { - // String topicValue = value.toString(); - logger.debug(topic + " " + value); - - if (topic2observation.containsKey(topic)) { - updateObservationValue(topic2observation.get(topic), value); - } - - // Check if value can be parsed with regex - // e.g. pepoli:6lowpan:network = | ID: NODO1 | Temperature: 24.60 | Humidity: - // 35.40 | Pressure: 1016.46 - - else if (appProfile.getExtendedData().get("regexTopics").getAsJsonObject().get(topic) != null) { - JsonArray arr = appProfile.getExtendedData().get("regexTopics").getAsJsonObject().get(topic) - .getAsJsonArray(); - for (JsonElement regex : arr) { - Pattern p = Pattern.compile(regex.getAsString()); - Matcher m = p.matcher(value); - if (m.matches()) { - String newTopic = topic.replace(":", "/"); - - for (int i = 1; i < m.groupCount(); i++) { - if (!m.group(i).equals(m.group("value"))) - newTopic += "/" + m.group(i); - } - - String newValue = m.group("value"); - - updateObservationValue(topic2observation.get(newTopic), newValue); - } - } - } - - // Check if value can be parsed with JSON - // e.g. {"moistureValue":3247, "nodeId":"device3", - // "timestamp":"2017-11-15T10:00:02.123028089Z"} - - else if (appProfile.getExtendedData().get("jsonTopics").getAsJsonObject().get(topic) != null) { - String idMember = appProfile.getExtendedData().get("jsonTopics").getAsJsonObject().get(topic) - .getAsJsonObject().get("id").getAsString(); - String valueMember = appProfile.getExtendedData().get("jsonTopics").getAsJsonObject().get(topic) - .getAsJsonObject().get("value").getAsString(); - - JsonObject json = new JsonParser().parse(value).getAsJsonObject(); - String topicSuffix = json.get(idMember).getAsString(); - - String newValue = json.get(valueMember).getAsString(); - - String newTopic = topic + "/" + topicSuffix; - - updateObservationValue(topic2observation.get(newTopic), newValue); - } else { - logger.warn("TOPIC NOT FOUND: " + topic + " = " + value); - } - } - - public boolean start() { - // Subscribe to observation-topic mapping - Response ret = subscribe(null); - - if (ret.isError()) { - logger.fatal("Failed to subscribe: " + ret); - return false; - } - SubscribeResponse results = (SubscribeResponse) ret; - onAddedResults(results.getBindingsResults()); - - if (getApplicationProfile().getExtendedData().get("simulate").getAsBoolean()) - simulator(); - else { - // MQTT: begin - JsonObject mqtt = getApplicationProfile().getExtendedData().get("mqtt").getAsJsonObject(); - - String url = mqtt.get("url").getAsString(); - int port = mqtt.get("port").getAsInt(); - JsonArray topics = mqtt.get("topics").getAsJsonArray(); - - topicsFilter = new String[topics.size()]; - int i = 0; - for (JsonElement topic : topics) { - topicsFilter[i] = topic.getAsString(); - i++; - } - - boolean sslEnabled = false; - if (mqtt.get("ssl") != null) - sslEnabled = mqtt.get("ssl").getAsBoolean(); - - String serverURI = null; - if (sslEnabled) { - serverURI = "ssl://" + url + ":" + String.format("%d", port); - } else { - serverURI = "tcp://" + url + ":" + String.format("%d", port); - } - - // Create client - logger.info("Creating MQTT client..."); - String clientID = MqttClient.generateClientId(); - logger.info("Client ID: " + clientID); - logger.info("Server URI: " + serverURI); - try { - mqttClient = new MqttClient(serverURI, clientID); - } catch (MqttException e) { - logger.error(e.getMessage()); - return false; - } - - // Connect - logger.info("Connecting..."); - MqttConnectOptions options = new MqttConnectOptions(); - if (sslEnabled) { - SSLSecurityManager sm; - try { - sm = new SSLSecurityManager("TLSv1", "sepa.jks", "sepa2017", "sepa2017"); - } catch (UnrecoverableKeyException | KeyManagementException | KeyStoreException - | NoSuchAlgorithmException | CertificateException | IOException e) { - logger.error(e.getMessage()); - return false; - } - logger.info("Set SSL security"); - try { - options.setSocketFactory(sm.getSSLContext().getSocketFactory()); - } catch (KeyManagementException | NoSuchAlgorithmException e) { - logger.error(e.getMessage()); - return false; - } - } - try { - mqttClient.connect(options); - } catch (MqttException e) { - logger.error(e.getMessage()); - } - - // Subscribe - mqttClient.setCallback(this); - logger.info("Subscribing..."); - try { - mqttClient.subscribe(topicsFilter); - } catch (MqttException e) { - logger.error(e.getMessage()); - return false; - } - - for(String topic:topicsFilter) logger.info("MQTT client " + clientID + " subscribed to " + serverURI + " Topic filter " + topic); - // MQTT: end - } - - return true; - } - - public void simulator() { - new Thread() { - public void run() { - // 6LowPan - // e.g. pepoli:6lowpan:network = | ID: NODO1 | Temperature: 24.60 | Humidity: - // 35.40 | Pressure: 1016.46 - String pattern6LowPan = " | ID: %s | Temperature: %.2f | Humidity: %.2f | Pressure: %.2f"; - String[] nodes6LowPan = { "NODO1", "NODO2", "NODO3" }; - String topicLowPan = "pepoli:6lowpan:network"; - - // LoRa - // e.g. {"moistureValue":3247, "nodeId":"device3", - // "timestamp":"2017-11-15T10:00:02.123028089Z"} - String patternLoRa = "{\"moistureValue\":%.2f, \"nodeId\":\"%s\", \"timestamp\":\"2017-11-15T10:00:02.123028089Z\"}"; - String[] nodesLoRa = { "device1", "device2" }; - String topicLoRa = "ground/lora/moisture"; - - while (true) { - - for (String node : nodes6LowPan) { - for (int j = 0; j < 100; j++) { - String value = String.format(pattern6LowPan, node, (float) j, (float)(100 - j), - (float)(10 * j)); - try { - mqttMessage(topicLowPan, value); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - - // try { - // Thread.sleep(1000); - // } catch (InterruptedException e) { - // return; - // } - } - - for (String device : nodesLoRa) { - for (int j = 0; j < 100; j++) { - String value = String.format(patternLoRa, (float) j, device); - try { - mqttMessage(topicLoRa, value); - } catch (Exception e) { - logger.error(e.getMessage()); - } - } - - // try { - // Thread.sleep(1000); - // } catch (InterruptedException e) { - // return; - // } - } - - for (int i = 0; i < 35; i = i + 5) { - for (String topic : topic2observation.keySet()) { - if (topic.startsWith(topicLowPan.replace(":", "/"))) - continue; - if (topic.startsWith(topicLoRa.replace(":", "/"))) - continue; - - try { - mqttMessage(topic, String.format("%d", 10 + i)); - } catch (Exception e) { - logger.error(e.getMessage()); - } - - // try { - // Thread.sleep(1000); - // } catch (InterruptedException e) { - //// return; - // } - - } - } - } - } - }.start(); - } - - public void stop() { - try { - if (topicsFilter != null) - mqttClient.unsubscribe(topicsFilter); - } catch (MqttException e1) { - logger.error("Failed to unsubscribe " + e1.getMessage()); - } - - try { - mqttClient.disconnect(); - } catch (MqttException e) { - logger.error("Failed to disconnect " + e.getMessage()); - } - - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java deleted file mode 100644 index d0002455..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java +++ /dev/null @@ -1,57 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.mqtt; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class ObservationSimulator extends Producer implements Runnable { - private static final Logger logger = LogManager.getLogger("MQTTHDDSimulator"); - - private int value = 15; - private long timeout = 2000; - private Bindings bindings = new Bindings(); - - public ObservationSimulator(ApplicationProfile appProfile,String observation) throws SEPAProtocolException { - super(appProfile, "UPDATE_OBSERVATION_VALUE"); - bindings.addBinding("observation", new RDFTermURI(observation)); - } - - public void simulate() { - value += 5; - if (value > 45) value = 15; - bindings.addBinding("value", new RDFTermLiteral(String.format("%d", value))); - update(bindings); - } - - @Override - public void run() { - while(true) { - try { - Thread.sleep(timeout); - } catch (InterruptedException e) { - return; - } - simulate(); - } - } - - public static void main(String[] args) throws SEPAProtocolException, SEPAPropertiesException, InterruptedException { - if (args.length != 2) { - logger.error("Usage: java -jar ObservationSimulator.jar "); - System.exit(1); - } - - ObservationSimulator sim = new ObservationSimulator(new ApplicationProfile(args[0]),args[1]); - - Thread th = new Thread(sim); - th.start(); - th.join(); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/GarbageCollector.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/GarbageCollector.java deleted file mode 100644 index 42f27630..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/GarbageCollector.java +++ /dev/null @@ -1,88 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.randomnumbers; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.pattern.Aggregator; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class GarbageCollector extends Aggregator { - private final Logger logger = LogManager.getLogger("GarbageCollector"); - private long numbers = 0; - - public GarbageCollector() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("randomNumbers.jsap"), "COUNT_NUMBERS", "DELETE_NUMBERS"); - } - - public boolean subscribe() { - Response ret; - - ret = super.subscribe(null); - - if (ret.isError()) - return false; - - SubscribeResponse results = (SubscribeResponse) ret; - - for (Bindings binding : results.getBindingsResults().getBindings()) { - numbers += Integer.parseInt(binding.getBindingValue("numbers")); - logger.info("Total numbers: " + numbers); - } - - if (numbers >= getApplicationProfile().getExtendedData().get("gcnumbers").getAsInt()) { - logger.info("Collecting triples..."); - update(null); - } - - return true; - } - - @Override - public void onAddedResults(BindingsResults results) { - numbers = 0; - for (Bindings binding : results.getBindings()) { - numbers += Integer.parseInt(binding.getBindingValue("numbers")); - logger.info("Total numbers: " + numbers + " GC numbers: " - + getApplicationProfile().getExtendedData().get("gcnumbers").getAsInt()); - } - - if (numbers >= getApplicationProfile().getExtendedData().get("gcnumbers").getAsInt()) { - logger.info("Collecting triples..."); - update(null); - } - - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanCalculator.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanCalculator.java deleted file mode 100644 index a7e827fb..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanCalculator.java +++ /dev/null @@ -1,114 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.randomnumbers; - -import java.util.UUID; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.Aggregator; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class MeanCalculator extends Aggregator { - private static final Logger logger = LogManager.getLogger("MeanCalculator"); - - private float mean = 0; - private long counter = 0; - private String meanURI; - - private Bindings forcedBindings = new Bindings(); - - private final String baseURI = "rnd:Mean-"; - - public MeanCalculator() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("randomNumbers.jsap"), "RANDOM_NUMBER", "UPDATE_MEAN"); - - meanURI = baseURI + UUID.randomUUID(); - - forcedBindings.addBinding("mean", new RDFTermURI(meanURI)); - } - - public boolean start() { - Response ret; - - ret = subscribe(null); - - if (ret.isError()) - return false; - - mean = 0; - counter = 0; - - // Update! - forcedBindings.addBinding("counter", new RDFTermLiteral(String.format("%d", counter))); - forcedBindings.addBinding("value", new RDFTermLiteral(String.format("%.3f", mean))); - update(forcedBindings); - - return true; - } - - public void stop() { - unsubscribe(); - - } - - @Override - public void onRemovedResults(BindingsResults results) { - for (Bindings result : results.getBindings()) { - logger.debug(result); - if (result.getBindingValue("value") == null) { - logger.warn("Value is null"); - continue; - } - float value; - try { - value = Float.parseFloat(result.getBindingValue("value").replace(",", ".")); - } catch (Exception e) { - logger.error(e.getMessage()); - continue; - } - counter++; - mean = ((mean * (counter - 1)) + value) / counter; - logger.info(" mean: " + meanURI + " value: " + mean + " counter: " + counter); - } - - // Update! - forcedBindings.addBinding("counter", new RDFTermLiteral(String.format("%d", counter))); - forcedBindings.addBinding("value", new RDFTermLiteral(String.format("%.3f", mean))); - update(forcedBindings); - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onAddedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanMonitor.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanMonitor.java deleted file mode 100644 index 1bdf05bd..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/MeanMonitor.java +++ /dev/null @@ -1,78 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.randomnumbers; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; - -public class MeanMonitor extends Consumer { - private static final Logger logger = LogManager.getLogger("MeanMonitor"); - - public MeanMonitor() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("randomNumbers.jsap"), "MEAN"); - } - - public boolean subscribe() { - Response ret; - - ret = super.subscribe(null); - - if (ret.isError()) - return false; - - SubscribeResponse results = (SubscribeResponse) ret; - - // Previous mean values - for (Bindings binding : results.getBindingsResults().getBindings()) { - logger.info(binding.getBindingValue("mean") + " : " - + Float.parseFloat(binding.getBindingValue("value").replaceAll(",", ".")) + " (values: " - + Integer.parseInt(binding.getBindingValue("counter")) + ")"); - } - - return true; - } - - @Override - public void onAddedResults(BindingsResults results) { - for (Bindings binding : results.getBindings()) { - logger.info(binding.getBindingValue("mean") + " : " - + Float.parseFloat(binding.getBindingValue("value").replaceAll(",", ".")) + " (values: " - + Integer.parseInt(binding.getBindingValue("counter")) + ")"); - } - - } - - @Override - public void onResults(ARBindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumberGenerator.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumberGenerator.java deleted file mode 100644 index d01d0f8a..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumberGenerator.java +++ /dev/null @@ -1,47 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.randomnumbers; - -import java.util.UUID; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Producer; - -public class RandomNumberGenerator extends Producer { - private static final Logger logger = LogManager.getLogger("RandomNumber"); - - private final String baseURI = "rnd:RandomNumber-"; - Bindings forcedBindings = new Bindings(); - - public RandomNumberGenerator() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new ApplicationProfile("randomNumbers.jsap"), "RANDOM_NUMBER"); - - new Thread() { - public void run() { - while(true) { - try { - Thread.sleep(getApplicationProfile().getExtendedData().get("sleep").getAsInt()); - } catch (InterruptedException e) { - return; - } - - String value = String.format("%.3f", 100 * Math.random()).replace(",", "."); - String number = baseURI + UUID.randomUUID(); - forcedBindings.addBinding("number", new RDFTermURI(number)); - - // Update! - logger.info(baseURI+" generate random value: "+value); - forcedBindings.addBinding("value", new RDFTermLiteral(value)); - update(forcedBindings); - } - } - }.start(); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumbersTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumbersTest.java deleted file mode 100644 index 0261bc28..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/randomnumbers/RandomNumbersTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.randomnumbers; - -import java.io.IOException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; - -public class RandomNumbersTest { - private static final Logger logger = LogManager.getLogger("RandomNumbers"); - - // The application profile (JSAP) - static ApplicationProfile myApp; - - // Aggregators - static MeanCalculator meanCalculator; - static GarbageCollector gc; - - // Consumers - static MeanMonitor meanMonitor; - - public static void main(String[] args) - throws SEPAPropertiesException, SEPAProtocolException, SEPASecurityException, IOException { - - // First we create the application profile - myApp = new ApplicationProfile("randomNumbers.jsap"); - - // We can use the JSAP file also to store application specific - // parameters (e.g., the number of random generator) - int nGen = myApp.getExtendedData().get("generators").getAsInt(); - logger.info("Number of generators: " + nGen); - - // We start the consumers so we can monitor what is happening... - meanMonitor = new MeanMonitor(); - if (!meanMonitor.subscribe()) { - logger.fatal("Failed to subscribe Mean Monitor"); - System.exit(1); - } - - meanCalculator = new MeanCalculator(); - if (!meanCalculator.start()) { - logger.fatal("Failed to subscribe Mean Calculator"); - System.exit(1); - } - - gc = new GarbageCollector(); - if (!gc.subscribe()) { - logger.fatal("Failed to subscribe Triples Counter"); - System.exit(1); - } - - // We create and start the specified number of producers - for (int i = 0; i < nGen; i++) { - new RandomNumberGenerator(); - } - - System.out.println("Press any key to exit..."); - System.in.read(); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/PopulateExperiment.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/PopulateExperiment.java deleted file mode 100644 index 98b9988d..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/PopulateExperiment.java +++ /dev/null @@ -1,63 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.streetlamps; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public class PopulateExperiment extends SmartLightingBenchmark { - public PopulateExperiment() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(); - } - - protected String tag = "LampExp"; - protected static SmartLightingBenchmark benchmark = null; - - // Data set - protected int roads[] = { 100, 100, 100, 10 }; - protected int roadSizes[] = { 10, 25, 50, 100 }; - - // Road subscriptions - protected int roadSubscriptionRoads[] = {}; - - // Lamp subscriptions - protected int lampSubscriptionRoads[][] = {}; - protected int lampSubscriptionLamps[][] = {}; - - @Override - public void reset() { - - } - - @Override - public void runExperiment() { - - } - - @Override - public void dataset() { - // Data set creation - int roadIndex = firstRoadIndex; - nRoads = 0; - for (int i = 0; i < roads.length; i++) { - roadIndex = addRoads(roads[i], roadSizes[i], roadIndex); - nRoads = nRoads + roads[i]; - } - } - - @Override - public void subscribe() { - - } - - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - - benchmark = new PopulateExperiment(); - - try { - benchmark.run(true, true, 5000); - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadExperiment.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadExperiment.java deleted file mode 100644 index 6c2c02a8..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadExperiment.java +++ /dev/null @@ -1,69 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.streetlamps; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public class RoadExperiment extends SmartLightingBenchmark { - - // Data set - protected int roads[] = { 100, 100, 100, 10 }; - protected int roadSizes[] = { 10, 25, 50, 100 }; - - // Road subscriptions - protected int roadSubscriptionRoads[] = { 6, 105, 204, 308 }; - - // Lamp subscriptions - protected int lampSubscriptionRoads[][] = { { 1, 5 }, { 101, 104 }, { 201, 203 }, { 301, 307 } }; - protected int lampSubscriptionLamps[][] = { { 1, 10 }, { 1, 25 }, { 1, 50 }, { 1, 100 } }; - - public RoadExperiment() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(); - } - - @Override - public void dataset() { - // Data set creation - int roadIndex = firstRoadIndex; - nRoads = 0; - for (int i = 0; i < roads.length; i++) { - roadIndex = addRoads(roads[i], roadSizes[i], roadIndex); - nRoads = nRoads + roads[i]; - } - } - - @Override - public void subscribe() { - // Road subscription - for (int i = 0; i < roadSubscriptionRoads.length; i++) - addRoadSubscription(roadSubscriptionRoads[i]); - - // Lamp subscriptions - for (int i = 0; i < lampSubscriptionRoads.length; i++) - for (int X = lampSubscriptionRoads[i][0]; X < lampSubscriptionRoads[i][1] + 1; X++) - for (int Y = lampSubscriptionLamps[i][0]; Y < lampSubscriptionLamps[i][1] + 1; Y++) - addLampSubscription(X, Y); - } - - @Override - public void runExperiment() { - for (int road = 1; road < nRoads + 1; road++) - updateRoad(road, new Integer(100)); - } - - @Override - public void reset() { - for (int road = 1; road < nRoads + 1; road++) - updateRoad(road, new Integer(0)); - } - - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - RoadExperiment benchmark = null; - - benchmark = new RoadExperiment(); - - benchmark.run(true, true, 5000); - - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadLightEx.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadLightEx.java deleted file mode 100644 index cfcce770..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/RoadLightEx.java +++ /dev/null @@ -1,50 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.streetlamps; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -public class RoadLightEx extends RoadExperiment { - - public RoadLightEx() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(); - } - - @Override - public void reset() { - super.reset(); - } - - @Override - public void runExperiment() { - super.runExperiment(); - } - - @Override - public void dataset() { - roads = new int[] { 1, 1, 1, 1 }; - roadSizes = new int[] { 10, 25, 50, 100 }; - super.dataset(); - } - - @Override - public void subscribe() { - // Road subscriptions - roadSubscriptionRoads = new int[] { 1, 2, 3, 4 }; - - // Lamp subscriptions - lampSubscriptionRoads = new int[][] { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 } }; - lampSubscriptionLamps = new int[][] { { 1, 10 }, { 1, 25 }, { 1, 50 }, { 1, 100 } }; - super.subscribe(); - } - - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - RoadLightEx benchmark = null; - - benchmark = new RoadLightEx(); - - benchmark.run(true, true, 5000); - - } - -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/SmartLightingBenchmark.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/SmartLightingBenchmark.java deleted file mode 100644 index 8e8b93d0..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/streetlamps/SmartLightingBenchmark.java +++ /dev/null @@ -1,499 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.streetlamps; - -import java.util.Vector; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; -import it.unibo.arces.wot.sepa.pattern.Producer; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermLiteral; -import it.unibo.arces.wot.sepa.commons.sparql.RDFTermURI; - -public abstract class SmartLightingBenchmark { - // Benchmark definition - private Vector roadPoll = new Vector(); - private Vector roadSubscriptions = new Vector(); - private Vector lampSubscriptions = new Vector(); - - private Vector roadSubs = new Vector(); - private Vector lampSubs = new Vector(); - - private Producer lampUpdater; - private Producer roadUpdater; - - private static final Logger logger = LogManager.getLogger("SmartLightingBenchmark"); - - private int lampNotifyN = 0; - private int roadNotifyN = 0; - - public abstract void reset(); - - public abstract void runExperiment(); - - public abstract void dataset(); - - public abstract void subscribe(); - - // Data set - protected int firstRoadIndex = 1; - protected int nRoads = 0; - - static ApplicationProfile appProfile = null; - - private class RoadPool { - private final int size; - private final int number; - private final int firstIndex; - - public RoadPool(int number, int size, int firstIndex) { - this.size = size; - this.number = number; - this.firstIndex = firstIndex; - } - - public int getSize() { - return size; - } - - public int getNumber() { - return number; - } - - public int getFirstIndex() { - return firstIndex; - } - } - - private class Lamp { - private final int road; - private final int post; - - public Lamp(int road, int post) { - this.road = road; - this.post = post; - } - - public int getRoad() { - return road; - } - - public int getPost() { - return post; - } - } - - public void addLampSubscription(int roadIndex, int lampIndex) { - lampSubscriptions.addElement(new Lamp(roadIndex, lampIndex)); - } - - public void addRoadSubscription(int roadIndex) { - roadSubscriptions.addElement(roadIndex); - } - - public int addRoad(int size, int index) { - return addRoads(1, size, index); - } - - public int addRoads(int number, int size, int firstIndex) { - roadPoll.add(new RoadPool(number, size, firstIndex)); - return firstIndex + number; - } - - public SmartLightingBenchmark() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - appProfile = new ApplicationProfile("LightingBenchmark.jsap"); - lampUpdater = new Producer(appProfile, "UPDATE_LAMP"); - roadUpdater = new Producer(appProfile, "UPDATE_ROAD"); - } - - private synchronized int incrementLampNotifies() { - lampNotifyN++; - return lampNotifyN; - } - - private synchronized int incrementRoadNotifies() { - roadNotifyN++; - return roadNotifyN; - } - - class LampSubscription extends Consumer implements Runnable { - private String lampURI = ""; - private boolean running = true; - private Bindings bindings = new Bindings(); - private Object sync = new Object(); - - public LampSubscription(ApplicationProfile appProfile, int roadIndex, int lampIndex) - throws SEPAProtocolException, SEPASecurityException { - super(appProfile, "LAMP"); - lampURI = "bench:Lamp_" + roadIndex + "_" + lampIndex; - bindings.addBinding("lamp", new RDFTermURI(lampURI)); - } - - public boolean subscribe() { - long startTime = System.nanoTime(); - Response ret; - - ret = super.subscribe(bindings); - - long stopTime = System.nanoTime(); - logger.info("SUBSCRIBE LAMP " + lampURI + " " + (stopTime - startTime)); - - return ret.getClass().equals(SubscribeResponse.class); - } - - public void terminate() { - synchronized (sync) { - running = false; - sync.notify(); - } - } - - @Override - public void run() { - synchronized (sync) { - running = true; - while (running) { - try { - sync.wait(); - } catch (InterruptedException e) { - } - } - } - } - - @Override - public void onResults(ARBindingsResults results) { - incrementLampNotifies(); - } - - @Override - public void onAddedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - - } - - class RoadSubscription extends Consumer implements Runnable { - private String roadURI = ""; - private boolean running = true; - Bindings bindings = new Bindings(); - private Object sync = new Object(); - - public RoadSubscription(ApplicationProfile appProfile, int index) - throws SEPAProtocolException, SEPASecurityException { - super(appProfile, "ROAD"); - roadURI = "bench:Road_" + index; - bindings.addBinding("?road", new RDFTermURI(roadURI)); - } - - public boolean subscribe() { - long startTime = System.nanoTime(); - Response ret; - - ret = super.subscribe(bindings); - - long stopTime = System.nanoTime(); - logger.info("SUBSCRIBE ROAD " + roadURI + " " + (stopTime - startTime)); - - return ret.getClass().equals(SubscribeResponse.class); - } - - public void terminate() { - synchronized (sync) { - running = false; - sync.notify(); - } - } - - @Override - public void run() { - synchronized (sync) { - running = true; - while (running) { - try { - sync.wait(); - } catch (InterruptedException e) { - } - } - } - } - - @Override - public void onResults(ARBindingsResults results) { - incrementRoadNotifies(); - } - - @Override - public void onAddedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onRemovedResults(BindingsResults results) { - // TODO Auto-generated method stub - - } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - } - - private boolean subscribeLamp(int roadIndex, int postIndex) throws SEPAProtocolException, SEPASecurityException { - LampSubscription sub = new LampSubscription(appProfile, roadIndex, postIndex); - new Thread(sub).start(); - lampSubs.add(sub); - return sub.subscribe(); - } - - private boolean subscribeRoad(int roadIndex) throws SEPAProtocolException, SEPASecurityException { - RoadSubscription sub = new RoadSubscription(appProfile, roadIndex); - roadSubs.add(sub); - new Thread(sub).start(); - return sub.subscribe(); - } - - private int populate(int nRoad, int nPost, int firstRoadIndex) throws SEPAProtocolException, SEPASecurityException { - - Producer road = new Producer(appProfile, "INSERT_ROAD"); - Producer addPost2Road = new Producer(appProfile, "ADD_POST"); - Producer sensor = new Producer(appProfile, "INSERT_SENSOR"); - Producer post = new Producer(appProfile, "INSERT_POST"); - Producer lamp = new Producer(appProfile, "INSERT_LAMP"); - Producer addSensor2post = new Producer(appProfile, "ADD_SENSOR"); - Producer addLamp2post = new Producer(appProfile, "ADD_LAMP"); - - logger.debug("Number of roads: " + nRoad + " Posts/road: " + nPost + " First road index: " + firstRoadIndex); - - Bindings bindings = new Bindings(); - - // int roadIndex = firstRoadIndex; - - for (int roadIndex = firstRoadIndex; roadIndex < firstRoadIndex + nRoad; roadIndex++) { - // while (nRoad>0) { - - String roadURI = "bench:Road_" + roadIndex; - - bindings.addBinding("road", new RDFTermURI(roadURI)); - - long startTime = System.nanoTime(); - Response ret = road.update(bindings); - long stopTime = System.nanoTime(); - logger.info("INSERT ROAD " + roadURI + " " + (stopTime - startTime) + " 1"); - - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - // int postNumber = nPost; - - for (int postIndex = 1; postIndex < nPost + 1; postIndex++) { - // while(postNumber>0) { - // URI - String postURI = "bench:Post_" + roadIndex + "_" + postIndex; - String lampURI = "bench:Lamp_" + roadIndex + "_" + postIndex; - String temparatureURI = "bench:Temperature_" + roadIndex + "_" + postIndex; - String presenceURI = "bench:Presence_" + roadIndex + "_" + postIndex; - - bindings.addBinding("post", new RDFTermURI(postURI)); - bindings.addBinding("lamp", new RDFTermURI(lampURI)); - - // New post - startTime = System.nanoTime(); - ret = post.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT POST " + postURI + " " + (stopTime - startTime) + " 3"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - // Add post to road - startTime = System.nanoTime(); - ret = addPost2Road.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT POST2ROAD " + postURI + " " + (stopTime - startTime) + " 1"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - // New lamp - startTime = System.nanoTime(); - ret = lamp.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT LAMP " + lampURI + " " + (stopTime - startTime) + " 4"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - // Add lamp to post - startTime = System.nanoTime(); - ret = addLamp2post.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT LAMP2POST " + lampURI + " " + (stopTime - startTime) + " 1"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - // New temperature sensor - bindings.addBinding("sensor", new RDFTermURI(temparatureURI)); - bindings.addBinding("type", new RDFTermURI("bench:TEMPERATURE")); - bindings.addBinding("unit", new RDFTermURI("bench:CELSIUS")); - bindings.addBinding("value", new RDFTermLiteral("0")); - - startTime = System.nanoTime(); - ret = sensor.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT SENSOR " + temparatureURI + " " + (stopTime - startTime) + " 5"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - startTime = System.nanoTime(); - ret = addSensor2post.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT SENSOR2POST " + temparatureURI + " " + (stopTime - startTime) + " 1"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - // New presence sensor - bindings.addBinding("sensor", new RDFTermURI(presenceURI)); - bindings.addBinding("type", new RDFTermURI("bench:PRESENCE")); - bindings.addBinding("unit", new RDFTermURI("bench:BOOLEAN")); - bindings.addBinding("value", new RDFTermLiteral("false")); - - startTime = System.nanoTime(); - ret = sensor.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT SENSOR " + presenceURI + " " + (stopTime - startTime) + " 5"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - - startTime = System.nanoTime(); - ret = addSensor2post.update(bindings); - stopTime = System.nanoTime(); - logger.info("INSERT SENSOR2POST " + presenceURI + " " + (stopTime - startTime) + " 1"); - if (!ret.getClass().equals(UpdateResponse.class)) - return firstRoadIndex; - } - } - - return firstRoadIndex + nRoad; - } - - protected boolean updateLamp(int nRoad, int nLamp, Integer dimming) { - String lampURI = "bench:Lamp_" + nRoad + "_" + nLamp; - Bindings bindings = new Bindings(); - bindings.addBinding("lamp", new RDFTermURI(lampURI)); - bindings.addBinding("dimming", new RDFTermLiteral(dimming.toString())); - - long startTime = System.nanoTime(); - Response ret = lampUpdater.update(bindings); - long stopTime = System.nanoTime(); - - logger.info("UPDATE LAMP " + lampURI + " " + (stopTime - startTime)); - - return ret.getClass().equals(UpdateResponse.class); - } - - protected boolean updateRoad(int nRoad, Integer dimming) { - String roadURI = "bench:Road_" + nRoad; - Bindings bindings = new Bindings(); - bindings.addBinding("?road", new RDFTermURI(roadURI)); - bindings.addBinding("?dimming", new RDFTermLiteral(dimming.toString())); - - long startTime = System.nanoTime(); - Response ret = roadUpdater.update(bindings); - long stopTime = System.nanoTime(); - - logger.info("UPDATE ROAD " + roadURI + " " + (stopTime - startTime)); - - return ret.getClass().equals(UpdateResponse.class); - } - - private void load() throws SEPAProtocolException, SEPASecurityException { - dataset(); - - for (RoadPool road : roadPoll) { - logger.debug("INSERT " + road.getNumber() + "x" + road.getSize() + " roads (" + road.getFirstIndex() + ":" - + (road.getFirstIndex() + road.getNumber() - 1) + ")"); - populate(road.getNumber(), road.getSize(), road.getFirstIndex()); - } - } - - private void waitNotifications(int delay) { - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - logger.debug(e.getMessage()); - } - - for (LampSubscription sub : lampSubs) { - - sub.unsubscribe(); - - sub.terminate(); - } - for (RoadSubscription sub : roadSubs) { - - sub.unsubscribe(); - - sub.terminate(); - } - } - - private void activateSubscriptions() throws SEPAProtocolException, SEPASecurityException{ - subscribe(); - - // SLAMP - for (Lamp lamp : lampSubscriptions) - subscribeLamp(lamp.getRoad(), lamp.getPost()); - - // SROAD - for (Integer index : roadSubscriptions) - subscribeRoad(index); - } - - public void run(boolean load, boolean reset, int delay) throws SEPAProtocolException, SEPASecurityException - { - if (load) - load(); - if (reset) - reset(); - activateSubscriptions(); - runExperiment(); - waitNotifications(delay); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index c9007733..c8fe04ce 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -110,12 +110,13 @@ import java.awt.event.MouseEvent; import javax.swing.border.EtchedBorder; import javax.swing.border.LineBorder; -import java.awt.SystemColor; +import javax.swing.JTextField; +import javax.swing.UIManager; public class Dashboard { private static final Logger logger = LogManager.getLogger("Dashboard"); - private static final String versionLabel = "SEPA Dashboard Ver 0.9.1"; + private static final String versionLabel = "SEPA Dashboard Ver 0.9.2"; private Properties appProperties = new Properties(); @@ -170,7 +171,7 @@ public void onSemanticEvent(Notification n) { } @Override - public void onBrokenSocket() { + public void onBrokenConnection() { } @@ -239,6 +240,8 @@ public void clear() { private JList subscribesList; ApplicationProfile appProfile; + private JTextField updateTimeout; + private JTextField queryTimeout; private class CopyAction extends AbstractAction { @@ -734,7 +737,7 @@ private boolean loadSAP(String file) { lblInfo.setToolTipText("JSAP loaded"); try { - sepaClient = new GenericClient(appProfile, handler); + sepaClient = new GenericClient(appProfile); } catch (SEPAProtocolException | SEPASecurityException e) { logger.error(e.getMessage()); System.exit(-1); @@ -845,6 +848,7 @@ public boolean isCellEditable(int row, int column) { panel_1.setLayout(gbl_panel_1); JLabel updateUrl = new JLabel("-"); + updateUrl.setForeground(UIManager.getColor("Desktop.background")); updateUrl.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_updateUrl = new GridBagConstraints(); gbc_updateUrl.gridwidth = 2; @@ -862,6 +866,7 @@ public boolean isCellEditable(int row, int column) { panel_1.add(lblUsinggraphuri, gbc_lblUsinggraphuri); JLabel lblUsinggraphuri_1 = new JLabel("using-graph-uri:"); + lblUsinggraphuri_1.setForeground(UIManager.getColor("Desktop.background")); GridBagConstraints gbc_lblUsinggraphuri_1 = new GridBagConstraints(); gbc_lblUsinggraphuri_1.anchor = GridBagConstraints.EAST; gbc_lblUsinggraphuri_1.insets = new Insets(0, 0, 5, 5); @@ -870,6 +875,7 @@ public boolean isCellEditable(int row, int column) { panel_1.add(lblUsinggraphuri_1, gbc_lblUsinggraphuri_1); JLabel updateUsingGraphUri = new JLabel("-"); + updateUsingGraphUri.setForeground(UIManager.getColor("Desktop.background")); updateUsingGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_updateUsingGraphUri = new GridBagConstraints(); gbc_updateUsingGraphUri.anchor = GridBagConstraints.WEST; @@ -879,6 +885,7 @@ public boolean isCellEditable(int row, int column) { panel_1.add(updateUsingGraphUri, gbc_updateUsingGraphUri); JLabel lblNamedgraphuri = new JLabel("using-named-graph-uri:"); + lblNamedgraphuri.setForeground(UIManager.getColor("Desktop.background")); GridBagConstraints gbc_lblNamedgraphuri = new GridBagConstraints(); gbc_lblNamedgraphuri.anchor = GridBagConstraints.EAST; gbc_lblNamedgraphuri.insets = new Insets(0, 0, 0, 5); @@ -887,6 +894,7 @@ public boolean isCellEditable(int row, int column) { panel_1.add(lblNamedgraphuri, gbc_lblNamedgraphuri); JLabel updateNamedGraphUri = new JLabel("-"); + updateNamedGraphUri.setForeground(UIManager.getColor("Desktop.background")); updateNamedGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_updateNamedGraphUri = new GridBagConstraints(); gbc_updateNamedGraphUri.anchor = GridBagConstraints.WEST; @@ -910,6 +918,7 @@ public boolean isCellEditable(int row, int column) { panel_2.setLayout(gbl_panel_2); JLabel queryUrl = new JLabel("-"); + queryUrl.setForeground(UIManager.getColor("Desktop.background")); queryUrl.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_queryUrl = new GridBagConstraints(); gbc_queryUrl.gridwidth = 2; @@ -920,6 +929,7 @@ public boolean isCellEditable(int row, int column) { panel_2.add(queryUrl, gbc_queryUrl); JLabel subscribeUrl = new JLabel("-"); + subscribeUrl.setForeground(UIManager.getColor("Desktop.background")); subscribeUrl.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_subscribeUrl = new GridBagConstraints(); gbc_subscribeUrl.gridwidth = 2; @@ -930,6 +940,7 @@ public boolean isCellEditable(int row, int column) { panel_2.add(subscribeUrl, gbc_subscribeUrl); JLabel lblDefaultgraphuri = new JLabel("default-graph-uri:"); + lblDefaultgraphuri.setForeground(UIManager.getColor("Desktop.background")); GridBagConstraints gbc_lblDefaultgraphuri = new GridBagConstraints(); gbc_lblDefaultgraphuri.anchor = GridBagConstraints.EAST; gbc_lblDefaultgraphuri.insets = new Insets(0, 0, 5, 5); @@ -938,6 +949,7 @@ public boolean isCellEditable(int row, int column) { panel_2.add(lblDefaultgraphuri, gbc_lblDefaultgraphuri); JLabel queryDefaultGraphUri = new JLabel("-"); + queryDefaultGraphUri.setForeground(UIManager.getColor("Desktop.background")); queryDefaultGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_queryDefaultGraphUri = new GridBagConstraints(); gbc_queryDefaultGraphUri.anchor = GridBagConstraints.WEST; @@ -947,6 +959,7 @@ public boolean isCellEditable(int row, int column) { panel_2.add(queryDefaultGraphUri, gbc_queryDefaultGraphUri); JLabel lblNamedgraphuri_1 = new JLabel("named-graph-uri:"); + lblNamedgraphuri_1.setForeground(UIManager.getColor("Desktop.background")); GridBagConstraints gbc_lblNamedgraphuri_1 = new GridBagConstraints(); gbc_lblNamedgraphuri_1.anchor = GridBagConstraints.EAST; gbc_lblNamedgraphuri_1.insets = new Insets(0, 0, 0, 5); @@ -955,6 +968,7 @@ public boolean isCellEditable(int row, int column) { panel_2.add(lblNamedgraphuri_1, gbc_lblNamedgraphuri_1); JLabel queryNamedGraphUri = new JLabel("-"); + queryNamedGraphUri.setForeground(UIManager.getColor("Desktop.background")); queryNamedGraphUri.setFont(new Font("Lucida Grande", Font.BOLD, 13)); GridBagConstraints gbc_queryNamedGraphUri = new GridBagConstraints(); gbc_queryNamedGraphUri.anchor = GridBagConstraints.WEST; @@ -981,6 +995,7 @@ public boolean isCellEditable(int row, int column) { panel_4.setLayout(gbl_panel_4); JLabel lblUpdates = new JLabel("UPDATES"); + lblUpdates.setForeground(UIManager.getColor("Desktop.background")); GridBagConstraints gbc_lblUpdates = new GridBagConstraints(); gbc_lblUpdates.anchor = GridBagConstraints.NORTH; gbc_lblUpdates.insets = new Insets(0, 0, 5, 0); @@ -1043,6 +1058,7 @@ public void valueChanged(ListSelectionEvent e) { panel_5.setLayout(gbl_panel_5); JLabel lblForcedBindings = new JLabel("Forced bindings"); + lblForcedBindings.setForeground(UIManager.getColor("CheckBox.select")); GridBagConstraints gbc_lblForcedBindings = new GridBagConstraints(); gbc_lblForcedBindings.anchor = GridBagConstraints.NORTH; gbc_lblForcedBindings.insets = new Insets(0, 0, 5, 0); @@ -1078,6 +1094,7 @@ public void valueChanged(ListSelectionEvent e) { panel_6.setLayout(gbl_panel_6); JLabel lblSubscribes = new JLabel("QUERIES"); + lblSubscribes.setForeground(UIManager.getColor("Desktop.background")); GridBagConstraints gbc_lblSubscribes = new GridBagConstraints(); gbc_lblSubscribes.anchor = GridBagConstraints.NORTH; gbc_lblSubscribes.insets = new Insets(0, 0, 5, 0); @@ -1100,30 +1117,32 @@ public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { if (subscribesList.getSelectedIndex() != -1) { - String sparql = appProfile.getSPARQLQuery(subscribesList.getSelectedValue()); + String ID = subscribesList.getSelectedValue(); + + String sparql = appProfile.getSPARQLQuery(ID); sparql = sparql.replaceFirst("\n", ""); sparql = sparql.replaceAll("\t", ""); sparql = sparql.trim(); SPARQLSubscribe.setText(sparql); - String request = appProfile.getQueryUrl(updatesList.getSelectedValue()); - if(appProfile.getQueryMethod().equals(HTTPMethod.GET)) request = "GET " + request; - else if(appProfile.getQueryMethod().equals(HTTPMethod.POST)) request = "POST " + request; - else if(appProfile.getQueryMethod().equals(HTTPMethod.URL_ENCODED_POST)) request = "URL ENCODED POST " + request; + String request = appProfile.getQueryUrl(ID); + if(appProfile.getQueryMethod(ID).equals(HTTPMethod.GET)) request = "GET " + request; + else if(appProfile.getQueryMethod(ID).equals(HTTPMethod.POST)) request = "POST " + request; + else if(appProfile.getQueryMethod(ID).equals(HTTPMethod.URL_ENCODED_POST)) request = "URL ENCODED POST " + request; queryUrl.setText(request); - subscribeUrl.setText(appProfile.getSubscribeUrl(updatesList.getSelectedValue())); + subscribeUrl.setText(appProfile.getSubscribeUrl(ID)); - queryDefaultGraphUri.setText(appProfile.getDefaultGraphURI(subscribesList.getSelectedValue())); - queryNamedGraphUri.setText(appProfile.getNamedGraphURI(subscribesList.getSelectedValue())); - } - - Bindings bindings = appProfile.getQueryBindings(subscribesList.getSelectedValue()); - subscribeForcedBindingsDM.clearBindings(); - if (bindings == null) - return; - for (String var : bindings.getVariables()) { - subscribeForcedBindingsDM.addBindings(var, bindings.isLiteral(var)); + queryDefaultGraphUri.setText(appProfile.getDefaultGraphURI(ID)); + queryNamedGraphUri.setText(appProfile.getNamedGraphURI(ID)); + + Bindings bindings = appProfile.getQueryBindings(ID); + subscribeForcedBindingsDM.clearBindings(); + if (bindings == null) + return; + for (String var : bindings.getVariables()) { + subscribeForcedBindingsDM.addBindings(var, bindings.isLiteral(var)); + } } } } @@ -1141,6 +1160,7 @@ public void valueChanged(ListSelectionEvent e) { panel_7.setLayout(gbl_panel_7); JLabel lblForcedBindings_1 = new JLabel("Forced bindings"); + lblForcedBindings_1.setForeground(UIManager.getColor("CheckBox.select")); GridBagConstraints gbc_lblForcedBindings_1 = new GridBagConstraints(); gbc_lblForcedBindings_1.anchor = GridBagConstraints.NORTH; gbc_lblForcedBindings_1.insets = new Insets(0, 0, 5, 0); @@ -1170,47 +1190,6 @@ public void valueChanged(ListSelectionEvent e) { scrollPane_Update.setViewportView(SPARQLUpdate); SPARQLUpdate.setLineWrap(true); - btnUpdate = new JButton("UPDATE"); - btnUpdate.setEnabled(false); - btnUpdate.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Bindings forced = new Bindings(); - for (int index = 0; index < updateForcedBindingsDM.getRowCount(); index++) { - String value = (String) updateForcedBindingsDM.getValueAt(index, 1); - String var = (String) updateForcedBindingsDM.getValueAt(index, 0); - boolean literal = (boolean) updateForcedBindingsDM.getValueAt(index, 2); - if (value.equals("")) { - lblInfo.setText("Please specify binding value: " + var); - lblInfo.setToolTipText("Please specify binding value: " + var); - return; - } - - if (literal) - forced.addBinding(var, new RDFTermLiteral(value)); - else - forced.addBinding(var, new RDFTermURI(value)); - } - - String update = SPARQLUpdate.getText().replaceAll("[\n\t]", ""); - - long start = System.currentTimeMillis(); - Response result; - try { - result = sepaClient.update(updatesList.getSelectedValue(),update, forced,appProfile.getUsingGraphURI(updatesList.getSelectedValue()),appProfile.getUsingNamedGraphURI(updatesList.getSelectedValue())); - } catch (SEPAProtocolException | SEPASecurityException e1) { - result = new ErrorResponse(500,e1.getMessage()); - } - long stop = System.currentTimeMillis(); - - String status = "DONE"; - if (result.isError()) { - status = "FAILED " + ((ErrorResponse) result).getErrorMessage(); - } - lblInfo.setText("UPDATE (" + (stop - start) + " ms): " + status); - lblInfo.setToolTipText("UPDATE (" + (stop - start) + " ms): " + status); - } - }); - JScrollPane scrollPane_Subscribe = new JScrollPane(); GridBagConstraints gbc_scrollPane_Subscribe = new GridBagConstraints(); gbc_scrollPane_Subscribe.fill = GridBagConstraints.BOTH; @@ -1222,11 +1201,102 @@ public void actionPerformed(ActionEvent e) { SPARQLSubscribe = new JTextArea(); scrollPane_Subscribe.setViewportView(SPARQLSubscribe); SPARQLSubscribe.setLineWrap(true); - GridBagConstraints gbc_btnUpdate = new GridBagConstraints(); - gbc_btnUpdate.insets = new Insets(0, 0, 5, 5); - gbc_btnUpdate.gridx = 0; - gbc_btnUpdate.gridy = 3; - primitives.add(btnUpdate, gbc_btnUpdate); + + JPanel panel_3 = new JPanel(); + GridBagConstraints gbc_panel_3 = new GridBagConstraints(); + gbc_panel_3.insets = new Insets(0, 0, 5, 5); + gbc_panel_3.fill = GridBagConstraints.BOTH; + gbc_panel_3.gridx = 0; + gbc_panel_3.gridy = 3; + primitives.add(panel_3, gbc_panel_3); + GridBagLayout gbl_panel_3 = new GridBagLayout(); + gbl_panel_3.columnWidths = new int[]{58, 108, 0}; + gbl_panel_3.rowHeights = new int[]{0, 0}; + gbl_panel_3.columnWeights = new double[]{0.0, 0.0, Double.MIN_VALUE}; + gbl_panel_3.rowWeights = new double[]{1.0, Double.MIN_VALUE}; + panel_3.setLayout(gbl_panel_3); + + btnUpdate = new JButton("UPDATE"); + btnUpdate.setForeground(UIManager.getColor("Desktop.background")); + GridBagConstraints gbc_btnUpdate = new GridBagConstraints(); + gbc_btnUpdate.fill = GridBagConstraints.HORIZONTAL; + gbc_btnUpdate.insets = new Insets(0, 0, 0, 5); + gbc_btnUpdate.gridx = 0; + gbc_btnUpdate.gridy = 0; + panel_3.add(btnUpdate, gbc_btnUpdate); + btnUpdate.setEnabled(false); + + JPanel panel_10 = new JPanel(); + GridBagConstraints gbc_panel_10 = new GridBagConstraints(); + gbc_panel_10.anchor = GridBagConstraints.WEST; + gbc_panel_10.fill = GridBagConstraints.VERTICAL; + gbc_panel_10.gridx = 1; + gbc_panel_10.gridy = 0; + panel_3.add(panel_10, gbc_panel_10); + GridBagLayout gbl_panel_10 = new GridBagLayout(); + gbl_panel_10.columnWidths = new int[]{80, 245, 0}; + gbl_panel_10.rowHeights = new int[]{0, 0}; + gbl_panel_10.columnWeights = new double[]{1.0, 0.0, Double.MIN_VALUE}; + gbl_panel_10.rowWeights = new double[]{0.0, Double.MIN_VALUE}; + panel_10.setLayout(gbl_panel_10); + + updateTimeout = new JTextField(); + GridBagConstraints gbc_updateTimeout = new GridBagConstraints(); + gbc_updateTimeout.fill = GridBagConstraints.HORIZONTAL; + gbc_updateTimeout.insets = new Insets(0, 0, 0, 5); + gbc_updateTimeout.gridx = 0; + gbc_updateTimeout.gridy = 0; + panel_10.add(updateTimeout, gbc_updateTimeout); + updateTimeout.setText("5000"); + updateTimeout.setColumns(10); + + JLabel lblTimeoutms = new JLabel("timeout (ms)"); + lblTimeoutms.setForeground(UIManager.getColor("Button.disabledText")); + GridBagConstraints gbc_lblTimeoutms = new GridBagConstraints(); + gbc_lblTimeoutms.fill = GridBagConstraints.HORIZONTAL; + gbc_lblTimeoutms.gridx = 1; + gbc_lblTimeoutms.gridy = 0; + panel_10.add(lblTimeoutms, gbc_lblTimeoutms); + btnUpdate.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Bindings forced = new Bindings(); + for (int index = 0; index < updateForcedBindingsDM.getRowCount(); index++) { + String value = (String) updateForcedBindingsDM.getValueAt(index, 1); + String var = (String) updateForcedBindingsDM.getValueAt(index, 0); + boolean literal = (boolean) updateForcedBindingsDM.getValueAt(index, 2); + if (value.equals("")) { + lblInfo.setText("Please specify binding value: " + var); + lblInfo.setToolTipText("Please specify binding value: " + var); + return; + } + + if (literal) + forced.addBinding(var, new RDFTermLiteral(value)); + else + forced.addBinding(var, new RDFTermURI(value)); + } + + String update = SPARQLUpdate.getText().replaceAll("[\n\t]", ""); + + long start = System.currentTimeMillis(); + Response result; + String ID = updatesList.getSelectedValue(); + try { + int timeout = Integer.parseInt(updateTimeout.getText()); + result = sepaClient.update(ID,update, forced,appProfile.getUsingGraphURI(ID),appProfile.getUsingNamedGraphURI(ID),appProfile.getUpdateMethod(ID),timeout); + } catch (SEPAProtocolException | SEPASecurityException | IOException e1) { + result = new ErrorResponse(500,e1.getMessage()); + } + long stop = System.currentTimeMillis(); + + String status = "DONE"; + if (result.isError()) { + status = "FAILED " + ((ErrorResponse) result).getErrorMessage(); + } + lblInfo.setText("UPDATE (" + (stop - start) + " ms): " + status); + lblInfo.setToolTipText("UPDATE (" + (stop - start) + " ms): " + status); + } + }); JPanel panel = new JPanel(); GridBagConstraints gbc_panel = new GridBagConstraints(); @@ -1236,162 +1306,200 @@ public void actionPerformed(ActionEvent e) { gbc_panel.gridy = 3; primitives.add(panel, gbc_panel); GridBagLayout gbl_panel = new GridBagLayout(); - gbl_panel.columnWidths = new int[] { 0, 0, 0 }; + gbl_panel.columnWidths = new int[] { 116, 126, 86, 0, 0 }; gbl_panel.rowHeights = new int[] { 0, 0 }; - gbl_panel.columnWeights = new double[] { 1.0, 1.0, Double.MIN_VALUE }; + gbl_panel.columnWeights = new double[] { 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; gbl_panel.rowWeights = new double[] { 1.0, Double.MIN_VALUE }; panel.setLayout(gbl_panel); - - btnQuery = new JButton("QUERY"); - btnQuery.setEnabled(false); - btnQuery.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Bindings forced = new Bindings(); - for (int index = 0; index < subscribeForcedBindings.getRowCount(); index++) { - String value = (String) subscribeForcedBindings.getValueAt(index, 1); - boolean literal = (boolean) subscribeForcedBindings.getValueAt(index, 2); - String var = (String) subscribeForcedBindings.getValueAt(index, 0); - - if (value.equals("")) { - lblInfo.setText("Please specify binding value: " + var); - lblInfo.setToolTipText("Please specify binding value: " + var); - return; - } - - if (literal) - forced.addBinding(var, new RDFTermLiteral(value)); - else - forced.addBinding(var, new RDFTermURI(value)); - } - - String query = SPARQLSubscribe.getText().replaceAll("[\n\t]", ""); - - lblInfo.setText("Running query..."); - long start = System.currentTimeMillis(); - response = sepaClient.query(query, forced); - long stop = System.currentTimeMillis(); - - String status = "DONE"; - if (response.isError()) { - status = "FAILED " + ((ErrorResponse) response).getErrorMessage(); - } else { - bindingsDM.clear(); - BindingsResults ret = ((QueryResponse) response).getBindingsResults(); - bindingsDM.setAddedResults(ret, null); - status = " " + ret.size() + " bindings results"; - } - - lblInfo.setText("QUERY (" + (stop - start) + " ms) :" + status); - lblInfo.setToolTipText("QUERY (" + (stop - start) + " ms) :" + status); - } - }); - GridBagConstraints gbc_btnQuery = new GridBagConstraints(); - gbc_btnQuery.insets = new Insets(0, 0, 0, 5); - gbc_btnQuery.gridx = 0; - gbc_btnQuery.gridy = 0; - panel.add(btnQuery, gbc_btnQuery); - - btnSubscribe = new JButton("SUBSCRIBE"); - btnSubscribe.setEnabled(false); - btnSubscribe.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (btnSubscribe.getText().equals("SUBSCRIBE")) { - Bindings forced = new Bindings(); - for (int index = 0; index < subscribeForcedBindings.getRowCount(); index++) { - String value = (String) subscribeForcedBindings.getValueAt(index, 1); - boolean literal = (boolean) subscribeForcedBindings.getValueAt(index, 2); - String var = (String) subscribeForcedBindings.getValueAt(index, 0); - - if (value.equals("")) { - lblInfo.setText("Please specify binding value: " + var); - lblInfo.setToolTipText("Please specify binding value: " + var); - return; + + btnSubscribe = new JButton("SUBSCRIBE"); + btnSubscribe.setForeground(UIManager.getColor("RadioButton.select")); + btnSubscribe.setEnabled(false); + btnSubscribe.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (btnSubscribe.getText().equals("SUBSCRIBE")) { + Bindings forced = new Bindings(); + for (int index = 0; index < subscribeForcedBindings.getRowCount(); index++) { + String value = (String) subscribeForcedBindings.getValueAt(index, 1); + boolean literal = (boolean) subscribeForcedBindings.getValueAt(index, 2); + String var = (String) subscribeForcedBindings.getValueAt(index, 0); + + if (value.equals("")) { + lblInfo.setText("Please specify binding value: " + var); + lblInfo.setToolTipText("Please specify binding value: " + var); + return; + } + ; + + if (literal) + forced.addBinding(var, new RDFTermLiteral(value)); + else + forced.addBinding(var, new RDFTermURI(value)); + } + + String query = SPARQLSubscribe.getText().replaceAll("[\n\t]", ""); + String ID = subscribesList.getSelectedValue(); + + try { + response = sepaClient.subscribe(ID, query, forced, appProfile.getDefaultGraphURI(ID), + appProfile.getNamedGraphURI(ID), handler); + } catch (SEPAProtocolException | SEPASecurityException e1) { + lblInfo.setText(e1.getMessage()); + return; + } + + if (response.getClass().equals(ErrorResponse.class)) { + lblInfo.setText(response.toString()); + lblInfo.setToolTipText(response.toString()); + return; + } + + // SPUID and results + String spuid = ((SubscribeResponse) response).getSpuid(); + BindingsResults ret = ((SubscribeResponse) response).getBindingsResults(); + + // Subscription panel + JPanel sub = new JPanel(); + + // Results label + JLabel infoLabel = new JLabel(); + infoLabel.setText("Subscribed. First results: " + ret.size()); + subscriptionResultsLabels.put(spuid, infoLabel); + + // Results table + subscriptionResultsDM.put(spuid, new BindingsTableModel()); + JTable bindingsResultsTable = new JTable(subscriptionResultsDM.get(spuid)); + bindingsResultsTable.setDefaultRenderer(Object.class, bindingsRender); + bindingsResultsTable.setAutoCreateRowSorter(true); + bindingsResultsTable.registerKeyboardAction(new CopyAction(), + KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + JComponent.WHEN_FOCUSED); + bindingsResultsTable.setCellSelectionEnabled(true); + subscriptionResultsTables.put(spuid, bindingsResultsTable); + subscriptionResultsDM.get(spuid).setAddedResults(ret, spuid); + JScrollPane bindingsResults = new JScrollPane(); + bindingsResults.setViewportView(bindingsResultsTable); + + // Unsubscribe button + JButton unsubscribeButton = new JButton(spuid); + unsubscribeButton.setEnabled(true); + unsubscribeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + response = sepaClient.unsubscribe(spuid); + + // if (response.isUnsubscribeResponse()) { + subscriptions.remove(sub); + subscriptionResultsDM.remove(spuid); + subscriptionResultsLabels.remove(spuid); + subscriptionResultsTables.remove(spuid); + // } + } + }); + + // Query label + JLabel queryLabel = new JLabel( + "" + query + " forced bindings: " + forced.toString() + ""); + queryLabel.setFont(new Font("Arial", Font.BOLD, 14)); + + // Layout + GridBagConstraints layoutFill = new GridBagConstraints(); + layoutFill.fill = GridBagConstraints.BOTH; + sub.setLayout(new BoxLayout(sub, BoxLayout.Y_AXIS)); + sub.setName(subscribesList.getSelectedValue()); + + // Add components + sub.add(queryLabel); + sub.add(unsubscribeButton); + sub.add(bindingsResults); + sub.add(infoLabel); + + subscriptions.add(sub, layoutFill); } - ; - - if (literal) - forced.addBinding(var, new RDFTermLiteral(value)); - else - forced.addBinding(var, new RDFTermURI(value)); - } - - String query = SPARQLSubscribe.getText().replaceAll("[\n\t]", ""); - - response = sepaClient.subscribe(query, forced); - - if (response.getClass().equals(ErrorResponse.class)) { - lblInfo.setText(response.toString()); - lblInfo.setToolTipText(response.toString()); - return; } - - // SPUID and results - String spuid = ((SubscribeResponse) response).getSpuid(); - BindingsResults ret = ((SubscribeResponse) response).getBindingsResults(); - - // Subscription panel - JPanel sub = new JPanel(); - - // Results label - JLabel infoLabel = new JLabel(); - infoLabel.setText("Subscribed. First results: " + ret.size()); - subscriptionResultsLabels.put(spuid, infoLabel); - - // Results table - subscriptionResultsDM.put(spuid, new BindingsTableModel()); - JTable bindingsResultsTable = new JTable(subscriptionResultsDM.get(spuid)); - bindingsResultsTable.setDefaultRenderer(Object.class, bindingsRender); - bindingsResultsTable.setAutoCreateRowSorter(true); - bindingsResultsTable.registerKeyboardAction(new CopyAction(), - KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), - JComponent.WHEN_FOCUSED); - bindingsResultsTable.setCellSelectionEnabled(true); - subscriptionResultsTables.put(spuid, bindingsResultsTable); - subscriptionResultsDM.get(spuid).setAddedResults(ret, spuid); - JScrollPane bindingsResults = new JScrollPane(); - bindingsResults.setViewportView(bindingsResultsTable); - - // Unsubscribe button - JButton unsubscribeButton = new JButton(spuid); - unsubscribeButton.setEnabled(true); - unsubscribeButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - response = sepaClient.unsubscribe(spuid); - - // if (response.isUnsubscribeResponse()) { - subscriptions.remove(sub); - subscriptionResultsDM.remove(spuid); - subscriptionResultsLabels.remove(spuid); - subscriptionResultsTables.remove(spuid); - // } - } - }); - - // Query label - JLabel queryLabel = new JLabel( - "" + query + " forced bindings: " + forced.toString() + ""); - queryLabel.setFont(new Font("Arial", Font.BOLD, 14)); - - // Layout - GridBagConstraints layoutFill = new GridBagConstraints(); - layoutFill.fill = GridBagConstraints.BOTH; - sub.setLayout(new BoxLayout(sub, BoxLayout.Y_AXIS)); - sub.setName(subscribesList.getSelectedValue()); - - // Add components - sub.add(queryLabel); - sub.add(unsubscribeButton); - sub.add(bindingsResults); - sub.add(infoLabel); - - subscriptions.add(sub, layoutFill); - } - } - }); - GridBagConstraints gbc_btnSubscribe = new GridBagConstraints(); - gbc_btnSubscribe.gridx = 1; - gbc_btnSubscribe.gridy = 0; - panel.add(btnSubscribe, gbc_btnSubscribe); + }); + + btnQuery = new JButton("QUERY"); + btnQuery.setForeground(UIManager.getColor("Desktop.background")); + GridBagConstraints gbc_btnQuery = new GridBagConstraints(); + gbc_btnQuery.fill = GridBagConstraints.HORIZONTAL; + gbc_btnQuery.insets = new Insets(0, 0, 0, 5); + gbc_btnQuery.gridx = 0; + gbc_btnQuery.gridy = 0; + panel.add(btnQuery, gbc_btnQuery); + btnQuery.setEnabled(false); + btnQuery.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Bindings forced = new Bindings(); + for (int index = 0; index < subscribeForcedBindings.getRowCount(); index++) { + String value = (String) subscribeForcedBindings.getValueAt(index, 1); + boolean literal = (boolean) subscribeForcedBindings.getValueAt(index, 2); + String var = (String) subscribeForcedBindings.getValueAt(index, 0); + + if (value.equals("")) { + lblInfo.setText("Please specify binding value: " + var); + lblInfo.setToolTipText("Please specify binding value: " + var); + return; + } + + if (literal) + forced.addBinding(var, new RDFTermLiteral(value)); + else + forced.addBinding(var, new RDFTermURI(value)); + } + + String query = SPARQLSubscribe.getText().replaceAll("[\n\t]", ""); + + String ID = subscribesList.getSelectedValue(); + + lblInfo.setText("Running query..."); + long start = System.currentTimeMillis(); + try { + int timeout = Integer.parseInt(queryTimeout.getText()); + response = sepaClient.query(ID,query, forced,appProfile.getDefaultGraphURI(ID),appProfile.getNamedGraphURI(ID),appProfile.getQueryMethod(ID),timeout); + } catch (SEPAProtocolException | SEPASecurityException | IOException e1) { + lblInfo.setText(e1.getMessage()); + return; + } + long stop = System.currentTimeMillis(); + + String status = "DONE"; + if (response.isError()) { + status = "FAILED " + ((ErrorResponse) response).getErrorMessage(); + } else { + bindingsDM.clear(); + BindingsResults ret = ((QueryResponse) response).getBindingsResults(); + bindingsDM.setAddedResults(ret, null); + status = " " + ret.size() + " bindings results"; + } + + lblInfo.setText("QUERY (" + (stop - start) + " ms) :" + status); + lblInfo.setToolTipText("QUERY (" + (stop - start) + " ms) :" + status); + } + }); + GridBagConstraints gbc_btnSubscribe = new GridBagConstraints(); + gbc_btnSubscribe.fill = GridBagConstraints.HORIZONTAL; + gbc_btnSubscribe.insets = new Insets(0, 0, 0, 5); + gbc_btnSubscribe.gridx = 1; + gbc_btnSubscribe.gridy = 0; + panel.add(btnSubscribe, gbc_btnSubscribe); + + queryTimeout = new JTextField(); + queryTimeout.setText("5000"); + GridBagConstraints gbc_queryTimeout = new GridBagConstraints(); + gbc_queryTimeout.insets = new Insets(0, 0, 0, 5); + gbc_queryTimeout.fill = GridBagConstraints.HORIZONTAL; + gbc_queryTimeout.gridx = 2; + gbc_queryTimeout.gridy = 0; + panel.add(queryTimeout, gbc_queryTimeout); + queryTimeout.setColumns(10); + + JLabel lblTimeoutms_1 = new JLabel("timeout (ms)"); + lblTimeoutms_1.setForeground(UIManager.getColor("Button.disabledText")); + GridBagConstraints gbc_lblTimeoutms_1 = new GridBagConstraints(); + gbc_lblTimeoutms_1.anchor = GridBagConstraints.WEST; + gbc_lblTimeoutms_1.gridx = 3; + gbc_lblTimeoutms_1.gridy = 0; + panel.add(lblTimeoutms_1, gbc_lblTimeoutms_1); JScrollPane bindingsResults = new JScrollPane(); GridBagConstraints gbc_bindingsResults = new GridBagConstraints(); @@ -1477,7 +1585,8 @@ public int getMaxCharactersPerLineCount() { }); JButton btnLoadXmlProfile = new JButton("Load JSAP"); - btnLoadXmlProfile.setBackground(SystemColor.textHighlight); + btnLoadXmlProfile.setForeground(UIManager.getColor("Button.light")); + btnLoadXmlProfile.setBackground(UIManager.getColor("Button.background")); GridBagConstraints gbc_btnLoadXmlProfile = new GridBagConstraints(); gbc_btnLoadXmlProfile.insets = new Insets(0, 0, 0, 5); gbc_btnLoadXmlProfile.gridx = 0; @@ -1567,6 +1676,8 @@ public void stateChanged(ChangeEvent e) { chckbxAutoscroll.setSelected(true); JButton btnClean = new JButton("Clear"); + btnClean.setForeground(UIManager.getColor("Button.light")); + btnClean.setBackground(UIManager.getColor("Separator.shadow")); GridBagConstraints gbc_btnClean = new GridBagConstraints(); gbc_btnClean.anchor = GridBagConstraints.NORTHWEST; gbc_btnClean.gridx = 5; diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Explorer.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Explorer.java deleted file mode 100644 index edd66835..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Explorer.java +++ /dev/null @@ -1,224 +0,0 @@ -package it.unibo.arces.wot.sepa.tools; - -import java.util.HashMap; - -import javax.swing.table.DefaultTableModel; -import javax.swing.tree.DefaultMutableTreeNode; - -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.Consumer; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; - -public class Explorer { - ApplicationProfile appProfile = null; - private DefaultTableModel propertiesDM; - - class PropertyMonitor extends Consumer { - OWLClassNodeModel root; - OWLClassNodeModel domain; - OWLClassNodeModel range; - - public PropertyMonitor(ApplicationProfile appProfile, String subscribeID) throws SEPAProtocolException, SEPASecurityException { - super(appProfile, subscribeID); - } - - @Override - public void onResults(ARBindingsResults notify) { - - } - - @Override - public void onAddedResults(BindingsResults bindingsResults) { - for (Bindings binding : bindingsResults.getBindings()) { - String propertyURI = ""; - String domainURI = ""; - String rangeURI = ""; - String comment = ""; - - if (binding.getBindingValue("property") != null) propertyURI =(binding.getBindingValue("property")); - if (binding.getBindingValue("domain") != null) domainURI = (binding.getBindingValue("domain")); - if (binding.getBindingValue("range") != null) rangeURI = (binding.getBindingValue("range")); - if (binding.getBindingValue("comment") != null) comment = binding.getBindingValue("comment"); - - if (propertyURI.equals("")) continue; - - propertiesDM.addRow(new String[]{propertyURI,domainURI,rangeURI,comment}); - } - - } - - @Override - public void onRemovedResults(BindingsResults bindingsResults) { - - - } - -// @Override -// public void onSubscribe(BindingsResults bindingsResults) { -// propertiesDM.getDataVector().clear(); -// -// for (Bindings binding : bindingsResults.getBindings()) { -// String propertyURI = ""; -// String domainURI = ""; -// String rangeURI = ""; -// String comment = ""; -// -// if (binding.getBindingValue("property") != null) propertyURI = (binding.getBindingValue("property")); -// if (binding.getBindingValue("domain") != null) domainURI = (binding.getBindingValue("domain")); -// if (binding.getBindingValue("range") != null) rangeURI = (binding.getBindingValue("range")); -// if (binding.getBindingValue("comment") != null) comment = binding.getBindingValue("comment"); -// -// if (propertyURI.equals("")) continue; -// -// propertiesDM.addRow(new String[]{propertyURI,domainURI,rangeURI,comment}); -// } -// } -// -// @Override -// public void onUnsubscribe() { -// // TODO Auto-generated method stub -// -// } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - - } - - class ClassMonitor extends Consumer { - private HashMap treeMap = new HashMap(); - OWLClassNodeModel root; - - public ClassMonitor(ApplicationProfile appProfile, String subscribeID) throws SEPAProtocolException, SEPASecurityException { - super(appProfile, subscribeID); - } - - @Override - public void onResults(ARBindingsResults notify) {} - - @Override - public void onAddedResults(BindingsResults bindingsResults) { - for (Bindings binding : bindingsResults.getBindings()) { - String classURI = null; - String classLabel = null; - String classComment = null; - String subclassURI = null; - - if (binding.getBindingValue("class") != null) classURI = (binding.getBindingValue("class")); - if (binding.getBindingValue("subclass") != null) subclassURI = (binding.getBindingValue("subclass")); - if (binding.getBindingValue("label") != null) classLabel = binding.getBindingValue("label"); - if (binding.getBindingValue("comment") != null) classComment = binding.getBindingValue("comment"); - - OWLClassNodeModel classNode = null; - OWLClassNodeModel subclassNode = null; - - if (classURI != null) { - if (!treeMap.containsKey(classURI)) { - classNode = new OWLClassNodeModel(classURI); - root.add(classNode); - treeMap.put(classURI, classNode); - } - else { - classNode = treeMap.get(classURI); - } - - //Label & comment - if (classLabel != null) { - classNode.setLabel(classLabel); - if (classComment != null) classNode.setComment("URI: "+classURI+"\n\n"+classComment); - else classNode.setComment("URI: "+classURI); - } - else if (classComment != null) classNode.setComment(classComment); - } - - if (subclassURI != null) { - if (!treeMap.containsKey(subclassURI)){ - subclassNode = new OWLClassNodeModel(subclassURI); - treeMap.put(subclassURI, subclassNode); - } - else subclassNode = treeMap.get(subclassURI); - - classNode.add(subclassNode); - } - } - - } - - @Override - public void onRemovedResults(BindingsResults bindingsResults) { - - } - -// @Override -// public void onSubscribe(BindingsResults bindingsResults) { -// root = new OWLClassNodeModel("owl:Thing"); -// -// treeMap.clear(); -// -// onAddedResults(bindingsResults); -// } - - @Override - public void onBrokenSocket() { - // TODO Auto-generated method stub - - } - - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - - } - } - - static class OWLClassNodeModel extends DefaultMutableTreeNode { - - /** - * - */ - private static final long serialVersionUID = 6299628084428311514L; - - private String label = null; - private String uri = null; - private String comment = null; - - public OWLClassNodeModel(String uri) { - super(uri); - this.uri = uri; - } - - @Override - public String toString() { - if (label != null) return label; - return uri; - } - - public String getComment() { - if (comment == null) return ""; - return comment; - } - - public void setLabel(String label) { - this.label = label; - } - - public void setComment(String comment) { - this.comment = comment; - } - - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATest.java deleted file mode 100644 index 639151b8..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATest.java +++ /dev/null @@ -1,711 +0,0 @@ -/* This program can be used and extended to test a SEPA implementation and API - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.tools; - -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; -import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; - -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; - -public class SEPATest { - protected static final Logger logger = LogManager.getLogger("SEPATest"); - protected static Results results = new SEPATest().new Results(); - - // Subscription variables - protected static String spuid = null; - protected static boolean pingReceived = false; - protected static boolean notificationReceived = false; - protected static Object sync = new Object(); - - // SPARQL 1.1 SE Protocol client - protected static SPARQL11SEProtocol client; - protected static SPARQL11SEProperties properties; - - // Subscriptions handler - protected static TestNotificationHandler handler = new TestNotificationHandler(); - - protected static final long notificationMaxDelay = 2000; - protected static final long pingDelay = 5000; - - protected class Results { - private long failed; - private ArrayList results = new ArrayList(); - - public void addResult(String title, boolean success) { - results.add(new Result(title, success)); - if (!success) - failed++; - } - - public void print() { - if (failed > 0) - logger.error("*** TEST FAILED (" + failed + "/" + results.size() + ") ***"); - else - logger.info("*** ვაიმეე TEST PASSED (" + results.size() + ") ვაიმეე ***"); - int index = 1; - for (Result res : results) { - res.print(index++); - } - } - } - - protected class Result { - private String title; - private boolean success; - - public Result(String title, boolean success) { - this.title = title; - this.success = success; - } - - public String toString() { - if (success) - title = title + " [PASSED]"; - else - title = title + " [FAILED]"; - return title; - } - - public void print(int index) { - if (success) - logger.info(index + " " + toString()); - else - logger.error(index + " " + toString()); - } - } - - protected static class TestNotificationHandler implements ISubscriptionHandler { - - @Override - public void onBrokenSocket() { - logger.debug("Broken subscription"); - } - - @Override - public void onError(ErrorResponse errorResponse) { - synchronized (sync) { - logger.debug(errorResponse.toString()); - sync.notify(); - } - } - - @Override - public void onSemanticEvent(Notification notify) { - synchronized (sync) { - logger.debug(notify.toString()); - notificationReceived = true; - sync.notify(); - } - } - } - - protected static boolean updateTest(String sparql, boolean secure) { - - notificationReceived = false; - - UpdateRequest update = new UpdateRequest(sparql); - - if (!secure) - logger.debug(update.toString()); - else - logger.debug("SECURE " + update.toString()); - - Response response; - if (secure) - response = client.secureUpdate(update); - else - response = client.update(update); - - logger.debug(response.toString()); - - return response.isUpdateResponse(); - } - - protected static boolean queryTest(String sparql, String utf8, boolean secure) { - QueryRequest query = new QueryRequest(sparql); - - if (!secure) - logger.debug(query.toString()); - else - logger.debug("SECURE " + query.toString()); - - Response response; - if (!secure) - response = client.query(query); - else - response = client.secureQuery(query); - - logger.debug(response.toString()); - - if (response.isQueryResponse() && utf8 != null) { - QueryResponse queryResponse = (QueryResponse) response; - List results = queryResponse.getBindingsResults().getBindings(); - if (results.size() == 1) { - Bindings bindings = results.get(0); - if (bindings.isLiteral("o")) { - String value = bindings.getBindingValue("o"); - if (value.equals(utf8)) - return true; - } - } - - return false; - } - - return response.isQueryResponse(); - } - - protected static boolean subscribeTest(String sparql, boolean secure) { - - SubscribeRequest sub = new SubscribeRequest(sparql); - - if (secure) - logger.debug("SECURE " + sub.toString()); - else - logger.debug(sub.toString()); - - Response response; - - if (!secure) - response = client.subscribe(sub); - else - response = client.secureSubscribe(sub); - - logger.debug(response.toString()); - - if (response.isSubscribeResponse()) { - spuid = ((SubscribeResponse) response).getSpuid(); - return true; - } - - return false; - } - - protected static boolean waitPing() { - long delay = pingDelay + (pingDelay / 2); - synchronized (sync) { - pingReceived = false; - try { - logger.debug("Waiting ping in " + delay + " ms..."); - sync.wait(delay); - } catch (InterruptedException e) { - logger.error(e.getMessage()); - } - } - - return pingReceived; - } - - protected static boolean waitNotification() { - synchronized (sync) { - if (notificationReceived) - return true; - try { - sync.wait(notificationMaxDelay); - } catch (InterruptedException e) { - logger.error(e.getMessage()); - } - } - - return notificationReceived; - } - - protected static boolean unsubscribeTest(String spuid, boolean secure) { - - UnsubscribeRequest unsub = new UnsubscribeRequest(spuid); - - Response response; - if (!secure) - response = client.unsubscribe(unsub); - else - response = client.secureUnsubscribe(unsub); - - logger.debug(response.toString()); - - return response.isUnsubscribeResponse(); - } - - protected static boolean registrationTest(String id) { - Response response; - response = client.register(id); - return !response.getClass().equals(ErrorResponse.class); - } - - protected static boolean requestAccessTokenTest() { - Response response; - response = client.requestToken(); - - logger.debug(response.toString()); - - return !response.getClass().equals(ErrorResponse.class); - } - - public static void main(String[] args) throws SEPASecurityException { - boolean ret = false; - - try { - properties = new SPARQL11SEProperties("client.jpar"); - } catch (SEPAPropertiesException e2) { - logger.fatal("JSAP exception: " + e2.getMessage()); - System.exit(1); - } - try { - client = new SPARQL11SEProtocol(properties,handler); - } catch (SEPAProtocolException e2) { - logger.fatal(e2.getLocalizedMessage()); - System.exit(1); - } - - logger.warn("**********************************************************"); - logger.warn("*** SPARQL 1.1 SE Protocol Service test suite ***"); - logger.warn("**********************************************************"); - logger.warn("*** WARNING: the RDF store content will be ERASED ***"); - logger.warn("*** Do you want to continue (yes/no)? ***"); - logger.warn("**********************************************************"); - logger.warn("SPARQL 1.1 SE Protocol Service parameters: " + client.toString()); - Scanner scanner = new Scanner(System.in); - scanner.useDelimiter("\\n"); // "\\z" means end of input - String input = scanner.next(); - if (!input.equals("yes")) { - scanner.close(); - logger.info("Bye bye! :-)"); - System.exit(0); - } - logger.warn("**********************************************************"); - logger.warn("*** Are you sure (yes/no)? ***"); - logger.warn("**********************************************************"); - input = scanner.next(); - if (!input.equals("yes")) { - scanner.close(); - logger.info("Bye bye! :-)"); - System.exit(0); - } - scanner.close(); - - // UPDATE - ret = updateTest( - "prefix test: delete {?s ?p ?o} insert {test:Sub test:Pred \"測試\"} where {?s ?p ?o}", - false); - - results.addResult("Update", ret); - if (ret) - logger.info("Update PASSED"); - else - logger.error("Update FAILED"); - - // QUERY - ret = queryTest("select ?o where {?s ?p ?o}", "測試", false); - - results.addResult("Query", ret); - if (ret) - logger.info("Query PASSED"); - else - logger.error("Query FAILED"); - - // SUBSCRIBE - - ret = subscribeTest("select ?o where {?s ?p ?o}", false); - - results.addResult("Subscribe - request", ret); - if (ret) - logger.info("Subscribe PASSED"); - else - logger.error("Subscribe FAILED"); - - // PING -// ret = waitPing(); -// results.addResult("Subscribe - ping", ret); -// if (ret) -// logger.info("Ping received PASSED"); -// else -// logger.error("Ping recevied FAILED"); - - // TRIGGER A NOTIFICATION - ret = updateTest( - "prefix test: delete {?s ?p ?o} insert {test:Sub test:Pred \"ვაიმეე\"} where {?s ?p ?o}", - false); - - results.addResult("Subscribe - triggering", ret); - if (ret) - logger.info("Triggering update PASSED"); - else - logger.error("Triggering update FAILED"); - - // WAIT NOTIFICATION - ret = waitNotification(); - results.addResult("Subscribe - notification", ret); - if (ret) - logger.info("Notification PASSED"); - else - logger.error("Notification FAILED"); - - // UNSUBSCRIBE - ret = unsubscribeTest(spuid, false); - - results.addResult("Unsubscribe - request", ret); - if (ret) - logger.info("Unsubscribe PASSED"); - else - logger.error("Unsubscribe FAILED"); - - // PING -// ret = !waitPing(); -// results.addResult("Unsubscribe - ping", ret); -// if (ret) -// logger.info("Ping not received PASSED"); -// else -// logger.error("Ping not recevied FAILED"); - - // ********************** - // Enable security - // ********************** - logger.debug("Switch to secure mode"); - - // REGISTRATION (registration not allowed) - ret = !registrationTest("RegisterMePlease"); - - results.addResult("Registration not allowed", ret); - if (ret) - logger.info("Registration not allowed PASSED"); - else - logger.error("Registration not allowed FAILED"); - - // REGISTRATION - ret = registrationTest("SEPATest"); - - results.addResult("Registration", ret); - if (ret) - logger.info("Registration PASSED"); - else - logger.error("Registration FAILED"); - - // REQUEST ACCESS TOKEN - ret = requestAccessTokenTest(); - results.addResult("Access token", ret); - if (ret) - logger.info("Access token PASSED"); - else - logger.error("Access token FAILED"); - - // REQUEST ACCESS TOKEN (not expired); - try { - if (!properties.isTokenExpired()) - ret = !requestAccessTokenTest(); - else - ret = false; - } catch (SEPASecurityException e2) { - logger.error(e2.getMessage()); - ret = false; - } - results.addResult("Access token not expired", ret); - if (ret) - logger.info("Access token (not expired) PASSED"); - else - logger.error("Access token (not expired) FAILED"); - - // REQUEST ACCESS TOKEN (expired); - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e2) { - logger.error(e2.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - try { - if (properties.isTokenExpired()) - ret = requestAccessTokenTest(); - else - ret = false; - } catch (SEPASecurityException e2) { - logger.error(e2.getMessage()); - ret = false; - } - results.addResult("Access token expired", ret); - if (ret) - logger.info("Access token (expired) PASSED"); - else - logger.error("Access token (expired) FAILED"); - - // SECURE UPDATE - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e2) { - logger.error(e2.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - try { - if (properties.isTokenExpired()) - requestAccessTokenTest(); - } catch (SEPASecurityException e13) { - logger.error(e13.getMessage()); - } - - ret = updateTest( - "prefix test: delete {?s ?p ?o} insert {test:Sub test:Pred \"ვაიმეე\"} where {?s ?p ?o}", - true); - - results.addResult("Secure update ", ret); - if (ret) - logger.info("Secure update PASSED"); - else - logger.error("Secure update FAILED"); - - // SECURE UPDATE (expired token) - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e12) { - logger.error(e12.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - - ret = !updateTest( - "prefix test: delete {?s ?p ?o} insert {test:Sub test:Pred \"vaimee!\"} where {?s ?p ?o}", - true); - - results.addResult("Secure update (expired)", ret); - if (ret) - logger.info("Secure update (expired) PASSED"); - else - logger.error("Secure update (expired) FAILED"); - - // SECURE QUERY - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e11) { - logger.error(e11.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - try { - if (properties.isTokenExpired()) - requestAccessTokenTest(); - } catch (SEPASecurityException e10) { - logger.error(e10.getMessage()); - } - - ret = queryTest("select ?o where {?s ?p ?o}", "ვაიმეე", true); - - results.addResult("Secure query", ret); - if (ret) - logger.info("Secure query PASSED"); - else - logger.error("Secure query FAILED"); - - // SECURE QUERY (expired token) - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e9) { - logger.error(e9.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - - ret = !queryTest("select ?o where {?s ?p ?o}", "ვაიმეე", true); - - results.addResult("Secure query (expired)", ret); - if (ret) - logger.info("Secure query (expired) PASSED"); - else - logger.error("Secure query (expired) FAILED"); - - // SECURE SUBSCRIBE - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e8) { - logger.error(e8.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - try { - if (properties.isTokenExpired()) - requestAccessTokenTest(); - } catch (SEPASecurityException e7) { - logger.error(e7.getMessage()); - } - - ret = subscribeTest("select ?o where {?s ?p ?o}", true); - - results.addResult("Secure subscribe - request", ret); - if (ret) - logger.info("Secure subscribe PASSED"); - else - logger.error("Secure subscribe FAILED"); - - // PING - ret = waitPing(); - results.addResult("Secure subscribe - ping", ret); - if (ret) - logger.info("Secure ping received PASSED"); - else - logger.error("Secure ping recevied FAILED"); - - // TRIGGER A NOTIFICATION - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e6) { - logger.error(e6.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - try { - if (properties.isTokenExpired()) - requestAccessTokenTest(); - } catch (SEPASecurityException e5) { - logger.error(e5.getMessage()); - } - - ret = updateTest( - "prefix test: delete {?s ?p ?o} insert {test:Sub test:Pred \"卢卡\"} where {?s ?p ?o}", - true); - - results.addResult("Secure subscribe - triggering", ret); - if (ret) - logger.info("Secure triggering update PASSED"); - else - logger.error("Secure triggering update FAILED"); - - // NOTIFICATION - ret = waitNotification(); - results.addResult("Secure subscribe - notification", ret); - if (ret) - logger.info("Secure subscribe - notification PASSED"); - else - logger.error("Secure subscribe - notification FAILED"); - - // SECURE UNSUBSCRIBE (expired) - try { - logger.debug("Wait token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e4) { - logger.error(e4.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - - ret = !unsubscribeTest(spuid, true); - - results.addResult("Secure unsubscribe (expired) - request", ret); - - if (ret) - logger.info("Secure unsubscribe (expired) - request PASSED"); - else - logger.error("Secure unsubscribe (expired) - request FAILED"); - - // UNSUBSCRIBE - try { - logger.debug("Waiting token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e3) { - logger.error(e3.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - try { - if (properties.isTokenExpired()) - requestAccessTokenTest(); - } catch (SEPASecurityException e2) { - logger.error(e2.getMessage()); - } - - ret = unsubscribeTest(spuid, true); - - results.addResult("Secure unsubscribe - request", ret); - if (ret) - logger.info("Secure unsubscribe - request PASSED"); - else - logger.error("Secure unsubscribe - request FAILED"); - - // WAITING PING - ret = !waitPing(); - results.addResult("Secure unsubscribe - ping", ret); - if (ret) - logger.info("Secure unsubscribe - ping PASSED"); - else - logger.error("Secure unsubscribe - ping FAILED"); - - // SECURE SUBSCRIBE (expired) - try { - logger.info("Wait token expiring in " + properties.getExpiringSeconds() + " + 2 seconds..."); - } catch (SEPASecurityException e1) { - logger.error(e1.getMessage()); - } - try { - Thread.sleep((properties.getExpiringSeconds() + 2) * 1000); - } catch (InterruptedException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - - ret = !subscribeTest("select ?o where {?s ?p ?o}", true); - - results.addResult("Secure subscribe (expired) - request", ret); - if (ret) - logger.info("Secure subscribe (expired) - request PASSED"); - else - logger.error("Secure subscribe (expired) - request FAILED"); - - results.print(); - - System.exit(0); - } -} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java deleted file mode 100644 index 80a08c1d..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java +++ /dev/null @@ -1,397 +0,0 @@ -/* This program can be used and extended to test a SEPA implementation and API - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.tools; - -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; - -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.pattern.ApplicationProfile; -import it.unibo.arces.wot.sepa.pattern.GenericClient; -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; - -public class SEPATestClient { - protected Results results; - protected String spuid = null; - protected boolean notificationReceived = false; - protected Object sync = new Object(); - - protected long notificationMaxDelay = 2000; - protected JsonArray sequence; - - private ApplicationProfile appProfile; - private SubscriptionHandler handler = new SubscriptionHandler(); - private GenericClient client; - - class SubscriptionHandler implements ISubscriptionHandler { - @Override - public void onSemanticEvent(Notification notify) { - synchronized (sync) { - System.out.println(notify.toString()); - notificationReceived = true; - sync.notify(); - } - } - - @Override - public void onBrokenSocket() { - System.out.println("Broken socket!"); - } - - @Override - public void onError(ErrorResponse errorResponse) { - synchronized (sync) { - System.out.println(errorResponse.toString()); - sync.notify(); - } - } - } - - public SEPATestClient(ApplicationProfile appProfile) throws SEPAProtocolException, SEPASecurityException { - client = new GenericClient(appProfile,handler); - - sequence = appProfile.getExtendedData().getAsJsonObject().get("sequence").getAsJsonArray(); - notificationMaxDelay = appProfile.getExtendedData().getAsJsonObject().get("notificationMaxDelay").getAsLong(); - - this.appProfile = appProfile; - } - - class Results { - private long failed; - private ArrayList results = new ArrayList(); - - public void addResult(String title, boolean success) { - results.add(new Result(title, success)); - if (!success) - failed++; - } - - public void print() { - if (failed > 0) - System.out.println("*** TEST FAILED (" + failed + "/" + results.size() + ") ***"); - else - System.out.println("*** ვაიმეე TEST PASSED (" + results.size() + ") ვაიმეე ***"); - int index = 1; - for (Result res : results) { - res.print(index++); - } - } - } - - class Result { - private String title; - private boolean success; - - public Result(String title, boolean success) { - this.title = title; - this.success = success; - } - - public String toString() { - if (success) - title = title + " [PASSED]"; - else - title = title + " [FAILED]"; - return title; - } - - public void print(int index) { - if (success) - System.out.println(index + " " + toString()); - else - System.out.println(index + " " + toString()); - } - } - - public boolean updateTest(String id, boolean secure) { - - notificationReceived = false; - - String sparql = appProfile.getSPARQLUpdate(id); - - if (!secure) - System.out.println("UPDATE: " + sparql); - else - System.out.println("SECURE UPDATE: " + sparql); - - Response response; - if (secure) - response = client.secureUpdate(sparql, null); - else - response = client.update(sparql, null); - - System.out.println(response.toString()); - - return response.isUpdateResponse(); - } - - public boolean queryTest(String id, int number, boolean secure) { - String sparql = appProfile.getSPARQLQuery(id); - - if (!secure) - System.out.println("QUERY: " + sparql); - else - System.out.println("SECURE QUERY: " + sparql); - - Response response; - if (!secure) - response = client.query(sparql, null); - else - response = client.secureQuery(sparql, null); - - System.out.println(response.toString()); - - if (response.isQueryResponse()) { - QueryResponse queryResponse = (QueryResponse) response; - List results = queryResponse.getBindingsResults().getBindings(); - return (results.size() == number); - } - - return false; - } - - public boolean subscribeTest(String id, long results, boolean secure) { - String sparql = appProfile.getSPARQLQuery(id); - - if (secure) - System.out.println("SECURE SUBSCRIBE: " + sparql); - else - System.out.println("SUBSCRIBE: " + sparql); - - Response response; - - if (!secure) - response = client.subscribe(sparql, null); - else - response = client.secureSubscribe(sparql, null); - - System.out.println(response.toString()); - - if (response.isSubscribeResponse()) { - spuid = ((SubscribeResponse) response).getSpuid(); - return ((SubscribeResponse) response).getBindingsResults().size() == results; - } - - return false; - } - - public boolean waitTokenToExpire() { - try { - System.out.println("Wait token to expire"); - Thread.sleep(client.getTokenExpiringSeconds()); - } catch (InterruptedException | SEPASecurityException e) { - return false; - } - - return true; - } - - public boolean waitNotification() { - synchronized (sync) { - if (notificationReceived) - return true; - try { - System.out.println("Wait notification"); - sync.wait(notificationMaxDelay); - } catch (InterruptedException e) { - System.out.println(e.getMessage()); - } - } - - return notificationReceived; - } - - public boolean unsubscribeTest(String spuid, boolean secure) { - Response response; - - if (secure) - System.out.println("SECURE UNSUBSCRIBE: " + spuid); - else - System.out.println("UNSUBSCRIBE: " + spuid); - - if (!secure) - response = client.unsubscribe(spuid); - else - response = client.secureUnsubscribe(spuid); - - System.out.println(response.toString()); - - return response.isUnsubscribeResponse(); - } - - public boolean registrationTest(String id) { - Response response; - - System.out.println("REGISTER: " + id); - - response = client.register(id); - - System.out.println(response.toString()); - - return !response.getClass().equals(ErrorResponse.class); - } - - public boolean requestAccessTokenTest() { - Response response; - - System.out.println("REQUEST ACCESS TOKEN"); - - response = client.requestToken(); - - System.out.println(response.toString()); - - return !response.getClass().equals(ErrorResponse.class); - } - - public void run() { - results = new Results(); - - boolean result = false; - String id = ""; - boolean secure = false; - boolean passed = true; - String name = ""; - String type = ""; - - for (JsonElement test : sequence) { - type = test.getAsJsonObject().get("type").getAsString(); - switch (type) { - case "UPDATE": - case "QUERY": - case "SUBSCRIBE": - id = test.getAsJsonObject().get("id").getAsString(); - secure = test.getAsJsonObject().get("secure").getAsBoolean(); - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - if (type.equals("UPDATE")) result = updateTest(id,secure); - else if (type.equals("QUERY")) result = queryTest(id,test.getAsJsonObject().get("results").getAsInt(),secure); - else result = subscribeTest(id,test.getAsJsonObject().get("results").getAsInt(),secure); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "UNSUBSCRIBE": - secure = test.getAsJsonObject().get("secure").getAsBoolean(); - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = unsubscribeTest(spuid,secure); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "REGISTER": - id = test.getAsJsonObject().get("id").getAsString(); - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = registrationTest(id); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "GET_TOKEN": - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = requestAccessTokenTest(); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "WAIT_TOKEN_TO_EXPIRE": - result = waitTokenToExpire(); - results.addResult("Wait token to expire", result); - break; - case "WAIT_NOTIFICATION": - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = waitNotification(); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - } - } - - results.print(); - } - - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - System.out.println("**********************************************************"); - System.out.println("*** SPARQL 1.1 SE Protocol Service test suite ***"); - System.out.println("**********************************************************"); - System.out.println("*** WARNING: the RDF store content will be ERASED ***"); - System.out.println("*** Do you want to continue (yes/no)? ***"); - System.out.println("**********************************************************"); - Scanner scanner = new Scanner(System.in); - scanner.useDelimiter("\\n"); // "\\z" means end of input - String input = scanner.next(); - if (!input.equals("yes")) { - scanner.close(); - System.out.println("Bye bye! :-)"); - System.exit(0); - } - System.out.println("**********************************************************"); - System.out.println("*** Are you sure (yes/no)? ***"); - System.out.println("**********************************************************"); - input = scanner.next(); - if (!input.equals("yes")) { - scanner.close(); - System.out.println("Bye bye! :-)"); - System.exit(0); - } - scanner.close(); - - SEPATestClient test = new SEPATestClient(new ApplicationProfile("sepatest.jsap")); - test.run(); - - //test = new SEPATestClient(new ApplicationProfile("sepatest-secure.jsap")); - //test.run(); - - System.exit(0); - } -} diff --git a/tools/src/main/resources/log4j2.xml b/tools/src/main/resources/log4j2.xml index a3ff3172..3be39197 100644 --- a/tools/src/main/resources/log4j2.xml +++ b/tools/src/main/resources/log4j2.xml @@ -1,20 +1,31 @@ - + + - - + + - - - + + + - \ No newline at end of file + From 047f5cae31d8c7f7a5996cdb53498e0ce0a51d0b Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 8 Jun 2018 08:59:35 +0200 Subject: [PATCH 08/76] # WARNING: head commit changed in the meantime Timeouts Dashboard updated --- .../commons/protocol/SPARQL11Protocol.java | 5 +- .../wot/sepa/pattern/ApplicationProfile.java | 20 +- .../sepa/engine/core/EngineProperties.java | 2 +- .../wot/sepa/engine/core/EventHandler.java | 2 - .../engine/dependability/CORSManager.java | 2 +- .../wot/sepa/engine/processing/Processor.java | 12 +- .../engine/processing/ProcessorMBean.java | 1 - .../engine/processing/QueryProcessor.java | 2 +- .../engine/processing/UpdateProcessor.java | 2 +- .../processing/subscriptions/SPUSync.java | 4 +- .../subscriptions/SubscribeProcessor.java | 12 +- .../processing/subscriptions/Subscriber.java | 4 +- .../processing/subscriptions/Unsubcriber.java | 4 +- .../sepa/engine/protocol/http/HttpGate.java | 4 +- .../engine/protocol/http/HttpUtilities.java | 10 +- .../sepa/engine/protocol/http/HttpsGate.java | 4 +- .../protocol/http/handler/EchoHandler.java | 4 - .../http/handler/JWTRequestHandler.java | 4 +- .../protocol/http/handler/QueryHandler.java | 2 +- .../http/handler/RegisterHandler.java | 4 +- .../http/handler/SPARQL11ResponseHandler.java | 2 +- .../http/handler/SecureQueryHandler.java | 3 - .../http/handler/SecureUpdateHandler.java | 3 - .../protocol/http/handler/UpdateHandler.java | 2 +- .../websocket/SecureWebsocketServer.java | 2 +- .../websocket/WebsocketEventHandler.java | 6 +- .../protocol/websocket/WebsocketServer.java | 2 +- .../wot/sepa/engine/scheduling/Scheduler.java | 4 +- engine/src/main/resources/log4j2.xml | 5 +- .../unibo/arces/wot/sepa/tools/Dashboard.java | 1524 +++++++++-------- 30 files changed, 906 insertions(+), 751 deletions(-) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index 5b93fc1e..1af413c7 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -34,7 +34,6 @@ import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; -import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -85,7 +84,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { try { // Execute HTTP request - logger.debug(req.toString()+" (timeout: " + request.getTimeout() + " ms) "); + logger.trace(req.toString()+" (timeout: " + request.getTimeout() + " ms) "); long start = Timings.getTime(); httpResponse = httpClient.execute(req); long stop = Timings.getTime(); @@ -100,7 +99,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { // Body responseEntity = httpResponse.getEntity(); responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); - logger.debug(String.format("Response code: %d", responseCode)); + logger.debug(String.format("Response code: %d #%d", responseCode,request.getToken())); EntityUtils.consume(responseEntity); // http://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/fundamentals.html#d5e279 diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java index ded7c6b6..3c45c33c 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/ApplicationProfile.java @@ -28,6 +28,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; @@ -145,10 +146,28 @@ public class ApplicationProfile extends SPARQL11SEProperties { public ApplicationProfile(String propertiesFile) throws SEPAPropertiesException { super(propertiesFile); + + if (!jsap.has("namespaces")) { + jsap.add("namespaces", new JsonObject()); + } + + if(!jsap.get("namespaces").getAsJsonObject().has("rdf")) jsap.get("namespaces").getAsJsonObject().add("rdf", new JsonPrimitive("http://www.w3.org/1999/02/22-rdf-syntax-ns#")); + if(!jsap.get("namespaces").getAsJsonObject().has("rdfs")) jsap.get("namespaces").getAsJsonObject().add("rdfs", new JsonPrimitive("http://www.w3.org/2000/01/rdf-schema#")); + if(!jsap.get("namespaces").getAsJsonObject().has("owl")) jsap.get("namespaces").getAsJsonObject().add("owl", new JsonPrimitive("http://www.w3.org/2002/07/owl#")); + if(!jsap.get("namespaces").getAsJsonObject().has("xsd")) jsap.get("namespaces").getAsJsonObject().add("xsd", new JsonPrimitive("http://www.w3.org/2001/XMLSchema#")); } public ApplicationProfile(String propertiesFile, byte[] secret) throws SEPAPropertiesException { super(propertiesFile, secret); + + if (!jsap.has("namespaces")) { + jsap.add("namespaces", new JsonObject()); + } + + if(!jsap.get("namespaces").getAsJsonObject().has("rdf")) jsap.get("namespaces").getAsJsonObject().add("rdf", new JsonPrimitive("http://www.w3.org/1999/02/22-rdf-syntax-ns#")); + if(!jsap.get("namespaces").getAsJsonObject().has("rdfs")) jsap.get("namespaces").getAsJsonObject().add("rdfs", new JsonPrimitive("http://www.w3.org/2000/01/rdf-schema#")); + if(!jsap.get("namespaces").getAsJsonObject().has("owl")) jsap.get("namespaces").getAsJsonObject().add("owl", new JsonPrimitive("http://www.w3.org/2002/07/owl#")); + if(!jsap.get("namespaces").getAsJsonObject().has("xsd")) jsap.get("namespaces").getAsJsonObject().add("xsd", new JsonPrimitive("http://www.w3.org/2001/XMLSchema#")); } protected Logger logger = LogManager.getLogger(); @@ -166,7 +185,6 @@ public JsonObject getExtendedData() { /* * UPDATE */ - public boolean isAuthenticationRequiredForUpdate(String id) { try { return jsap.get("updates").getAsJsonObject().get(id).getAsJsonObject().has("authentication"); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java index d2fe60f5..7abc6d60 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java @@ -43,7 +43,7 @@ * } } } */ public class EngineProperties { - private static final Logger logger = LogManager.getLogger("EngineProperties"); + private static final Logger logger = LogManager.getLogger(); private String defaultsFileName = "engine.jpar"; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java index 6ac3a48a..a3723a3d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java @@ -3,9 +3,7 @@ import java.io.IOException; import it.unibo.arces.wot.sepa.commons.response.Notification; -//import it.unibo.arces.wot.sepa.commons.response.Ping; public interface EventHandler extends ResponseHandler { public void notifyEvent(Notification notify) throws IOException; - //public void sendPing(Ping ping) throws IOException; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java index a430ac33..081ab400 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java @@ -109,7 +109,7 @@ public static boolean processCORSRequest(HttpAsyncExchange exchange){ if (!fieldNames.equals("")) exchange.getResponse().addHeader("Access-Control-Allow-Headers", fieldNames); for (Header head : exchange.getResponse().getAllHeaders()) - logger.debug("Header: ",head.getName()); + logger.debug(head); return true; } else { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index c0305d3f..9ef48af4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -97,8 +97,8 @@ public void run() { Request request = scheduledRequest.getRequest(); if (request.isUpdateRequest()) { - logger.info("Update request #" + request.getToken()); - logger.debug(request); + logger.debug("Update request #" + request.getToken()); + logger.trace(request); // Process update request request.setTimeout(ProcessorBeans.getUpdateTimeout()); @@ -111,8 +111,8 @@ public void run() { subscribeProcessor.process((UpdateResponse) ret); } } else if (request.isQueryRequest()) { - logger.info("Query request #" + request.getToken()); - logger.debug(request); + logger.debug("Query request #" + request.getToken()); + logger.trace(request); request.setTimeout(ProcessorBeans.getQueryTimeout()); @@ -125,8 +125,8 @@ public void run() { queryProcessing.setName("SEPA-Query-Processing-Thread-" + request.getToken()); queryProcessing.start(); } else if (request.isSubscribeRequest()) { - logger.info("Subscribe request #" + request.getToken()); - logger.debug(request); + logger.debug("Subscribe request #" + request.getToken()); + logger.trace(request); Response ret = subscribeProcessor.subscribe((SubscribeRequest) request, (EventHandler) scheduledRequest.getHandler()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java index 7e92a752..28311971 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java @@ -6,7 +6,6 @@ public interface ProcessorMBean { public long getProcessedRequests(); public long getProcessedQueryRequests(); -// public long getProcessedSPURequests(); public long getProcessedUpdateRequests(); public float getTimings_UpdateTime_ms(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index 7e8d17b0..4e80163b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -75,7 +75,7 @@ public synchronized Response process(QueryRequest req) { } long stop = Timings.getTime(); - logger.debug("Response: " + ret.toString()); + logger.trace("Response: " + ret.toString()); Timings.log("QUERY_PROCESSING_TIME", start, stop); ProcessorBeans.queryTimings(start, stop); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index b0191be9..3b8ea834 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -72,7 +72,7 @@ public synchronized Response process(UpdateRequest req) { } long stop = Timings.getTime(); - logger.debug("Response: " + ret.toString()); + logger.trace("Response: " + ret.toString()); Timings.log("UPDATE_PROCESSING_TIME", start, stop); ProcessorBeans.updateTimings(start, stop); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java index b863bc40..89f232d2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java @@ -11,7 +11,7 @@ import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; public class SPUSync { - private final Logger logger = LogManager.getLogger("SPUSync"); + private final Logger logger = LogManager.getLogger(); // SPU synchronization private HashSet processingSpus = new HashSet<>(); @@ -27,7 +27,7 @@ public void waitEndOfProcessing() { // Wait all SPUs completing processing (or timeout) synchronized (processingSpus) { while (!processingSpus.isEmpty()) { - logger.info(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); + logger.debug(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); try { processingSpus.wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); } catch (InterruptedException e) { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java index 54b2e479..824a9d5f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java @@ -42,7 +42,7 @@ import it.unibo.arces.wot.sepa.engine.core.EventHandler; public class SubscribeProcessor implements SubscribeProcessorMBean { - private final Logger logger = LogManager.getLogger("SubscribeProcessor"); + private final Logger logger = LogManager.getLogger(); private final Subscriber subscriber; private final Unsubcriber unsubscriber; @@ -85,7 +85,7 @@ public void stop(){ } public Response subscribe(SubscribeRequest req, EventHandler handler) { - logger.debug(req.toString()); + logger.trace(req.toString()); SubscribeProcessorBeans.subscribeRequest(); @@ -114,7 +114,7 @@ public Response subscribe(SubscribeRequest req, EventHandler handler) { } public Response unsubscribe(UnsubscribeRequest req) { - logger.debug(req); + logger.trace(req); SubscribeProcessorBeans.unsubscribeRequest(); @@ -129,10 +129,10 @@ public Response unsubscribe(UnsubscribeRequest req) { } public void process(UpdateResponse update) { - logger.info("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); + logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); Instant start = Instant.now(); - logger.info("Activate SPUs (Total: " + spuManager.size() + ")"); + logger.debug("Activate SPUs (Total: " + spuManager.size() + ")"); spuSync.startProcessing(spuManager.getAll()); @@ -149,7 +149,7 @@ public void process(UpdateResponse update) { Instant stop = Instant.now(); SubscribeProcessorBeans.timings(start, stop); - logger.info("*** PROCESSING SUBSCRIPTIONS END *** "); + logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java index d67406dc..7d529c25 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class Subscriber extends Thread { - private final Logger logger = LogManager.getLogger("Subscriber"); + private final Logger logger = LogManager.getLogger(); private final AtomicBoolean end = new AtomicBoolean(false); private final BlockingQueue subscriptionQueue; private final SPUManager spuManager; @@ -35,7 +35,7 @@ public void run() { SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); logger.debug(spu.getUUID() + " ACTIVATED (total: " + spuManager.size() + ")"); } catch (InterruptedException e) { - logger.info(e); + logger.debug(e); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java index aa3ca88b..9d11c9f7 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java @@ -14,7 +14,7 @@ * @see SPUManager */ public class Unsubcriber extends Thread { - private final Logger logger = LogManager.getLogger("Unsubscriber"); + private final Logger logger = LogManager.getLogger(); private final BlockingQueue unsubscribeQueue; private final SPUManager spuManager; private final AtomicBoolean end = new AtomicBoolean(false); @@ -38,7 +38,7 @@ public void run() { SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); logger.debug("Active SPUs: " + spuManager.size()); } catch (InterruptedException e) { - logger.info(e); + logger.debug(e); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java index fdf1e295..c3dca3fc 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java @@ -23,7 +23,7 @@ import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class HttpGate { - protected static final Logger logger = LogManager.getLogger("HttpGate"); + protected static final Logger logger = LogManager.getLogger(); protected EngineProperties properties; protected Scheduler scheduler; @@ -73,7 +73,7 @@ public void shutdown() { try { server.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); } catch (InterruptedException e) { - logger.info(serverInfo+" interrupted: " + e.getMessage()); + logger.debug(serverInfo+" interrupted: " + e.getMessage()); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java index e0b61a17..2aadf1c6 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java @@ -28,7 +28,7 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; public class HttpUtilities { - private static final Logger logger = LogManager.getLogger("Utilities"); + private static final Logger logger = LogManager.getLogger(); public static void sendResponse(HttpAsyncExchange exchange, int httpResponseCode, String body) { exchange.getResponse().setStatusCode(httpResponseCode); @@ -39,14 +39,6 @@ public static void sendResponse(HttpAsyncExchange exchange, int httpResponseCode public static void sendFailureResponse(HttpAsyncExchange exchange, int httpResponseCode, String responseBody) { - /*JsonObject json = new JsonObject(); - if (httpResponseCode != HttpStatus.SC_GATEWAY_TIMEOUT) - json = buildEchoResponse(exchange.getRequest()); - - json.add("body", new JsonPrimitive(responseBody)); - json.add("code", new JsonPrimitive(httpResponseCode)); - - sendResponse(exchange,httpResponseCode, json.toString());*/ ErrorResponse error = new ErrorResponse(httpResponseCode,responseBody); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java index 9ae3a102..60d40e55 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java @@ -28,7 +28,7 @@ import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class HttpsGate { - protected static final Logger logger = LogManager.getLogger("HttpsGate"); + protected static final Logger logger = LogManager.getLogger(); protected EngineProperties properties; protected Scheduler scheduler; @@ -97,7 +97,7 @@ public void shutdown() { try { server.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); } catch (InterruptedException e) { - logger.info(serverInfo+" interrupted: " + e.getMessage()); + logger.debug(serverInfo+" interrupted: " + e.getMessage()); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/EchoHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/EchoHandler.java index 3f66299f..0ce2d2b2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/EchoHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/EchoHandler.java @@ -15,14 +15,10 @@ import org.apache.http.nio.protocol.HttpAsyncRequestHandler; import org.apache.http.protocol.HttpContext; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; public class EchoHandler implements HttpAsyncRequestHandler { - protected static final Logger logger = LogManager.getLogger("EchoHandler"); public void handleInternal(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java index f246d535..d380913d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java @@ -20,7 +20,7 @@ import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; public class JWTRequestHandler implements HttpAsyncRequestHandler { - protected static final Logger logger = LogManager.getLogger("TokenRequestHandler"); + protected static final Logger logger = LogManager.getLogger(); private AuthorizationManager am; @@ -39,7 +39,7 @@ public HttpAsyncRequestConsumer processRequest(HttpRequest request, @Override public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpContext context) throws HttpException, IOException { - logger.info(">> REQUEST TOKEN"); + logger.debug(">> REQUEST TOKEN"); Header[] headers; // Parsing and validating request headers diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java index 7f48efea..f6cd5b43 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java @@ -108,7 +108,7 @@ protected Request parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolExcep } if (headers[0].getValue().equals("application/sparql-query")) { - logger.debug("query via POST directly"); + logger.trace("query via POST directly"); String requestUri = exchange.getRequest().getRequestLine().getUri(); String graphUri = null; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java index ac0d84c9..bcfd845b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java @@ -30,7 +30,7 @@ import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; public class RegisterHandler implements HttpAsyncRequestHandler { - private static final Logger logger = LogManager.getLogger("RegisterHandler"); + private static final Logger logger = LogManager.getLogger(); private AuthorizationManager am; @@ -47,7 +47,7 @@ public HttpAsyncRequestConsumer processRequest(HttpRequest request, @Override public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext context) throws HttpException, IOException { - logger.info(">> REGISTRATION"); + logger.debug(">> REGISTRATION"); String name = null; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java index 1ac680b9..f615bbb4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java @@ -36,7 +36,7 @@ public void sendResponse(Response response) { Timings.log(response); jmx.stop(handler); - logger.debug("Response #"+response.getToken()+" sent"); + logger.debug("Response sent #"+response.getToken()); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java index 25e6b449..9b477506 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java @@ -1,14 +1,11 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; import org.apache.http.HttpRequest; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureQueryHandler extends QueryHandler implements SecureQueryHandlerMBean { - protected static final Logger logger = LogManager.getLogger("SecureQueryHandler"); private AuthorizationManager am; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java index 01014234..08b4d67f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java @@ -1,14 +1,11 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; import org.apache.http.HttpRequest; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureUpdateHandler extends UpdateHandler implements SecureUpdateHandlerMBean { - protected static final Logger logger = LogManager.getLogger("SecureUpdateHandler"); private AuthorizationManager am; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java index 2fb2dc92..419aca79 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java @@ -41,7 +41,7 @@ * */ public class UpdateHandler extends SPARQL11Handler { - protected static final Logger logger = LogManager.getLogger("UpdateHandler"); + protected static final Logger logger = LogManager.getLogger(); public UpdateHandler(Scheduler scheduler) throws IllegalArgumentException { super(scheduler); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index ac54229a..ba76d9fa 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -29,7 +29,7 @@ public class SecureWebsocketServer extends WebsocketServer implements SecureWebsocketServerMBean { private AuthorizationManager oauth; - private Logger logger = LogManager.getLogger("SecureWebsocketServer"); + private final static Logger logger = LogManager.getLogger(); @Override protected String getWelcomeMessage() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java index 639f65e4..b672e309 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java @@ -18,7 +18,7 @@ import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; public class WebsocketEventHandler implements EventHandler { - private final Logger logger = LogManager.getLogger("WebsocketEventHandler"); + private static final Logger logger = LogManager.getLogger(); private WebSocket socket; private WebsocketBeans jmx; @@ -58,8 +58,8 @@ else if (response.isUnsubscribeResponse()) { dependabilityMng.onUnsubscribe(socket, ((UnsubscribeResponse)response).getSpuid()); } - logger.info("Response #"+response.getToken()+" ("+timing+" ms)"); - logger.debug(response); + logger.debug("Response #"+response.getToken()+" ("+timing+" ms)"); + logger.trace(response); send(response); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index 5648ad60..8978b576 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -33,7 +33,7 @@ import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class WebsocketServer extends WebSocketServer implements WebsocketServerMBean { - private final Logger logger = LogManager.getLogger("WebsocketServer"); + private static final Logger logger = LogManager.getLogger(); protected Scheduler scheduler; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 19640d35..b94e6dfb 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -107,7 +107,7 @@ private synchronized int getToken() { Integer token = tokens.get(0); tokens.removeElementAt(0); - logger.debug("Get token #" + token + " (Available: " + tokens.size() + ")"); + logger.trace("Get token #" + token + " (Available: " + tokens.size() + ")"); SchedulerBeans.tokenLeft(tokens.size()); @@ -128,7 +128,7 @@ private synchronized void releaseToken(Integer token) { logger.warn("Request to release a unused token: " + token + " (Available tokens: " + tokens.size() + ")"); } else { tokens.insertElementAt(token, tokens.size()); - logger.debug("Release token #" + token + " (Available: " + tokens.size() + ")"); + logger.trace("Release token #" + token + " (Available: " + tokens.size() + ")"); SchedulerBeans.tokenLeft(tokens.size()); } diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index 3be39197..94c68c8f 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -4,6 +4,9 @@ diff --git a/tools/swamp-demo.jsap b/tools/swamp-demo.jsap new file mode 100644 index 00000000..24945b3c --- /dev/null +++ b/tools/swamp-demo.jsap @@ -0,0 +1,292 @@ +{ + "host": "localhost", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/swamp/live", + "named-graph-uri": "http://wot.arces.unibo.it/swamp/live", + "using-graph-uri": "http://wot.arces.unibo.it/swamp/live", + "using-named-graph-uri": "http://wot.arces.unibo.it/swamp/live" + }, + "namespaces": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "sosa": "http://www.w3.org/ns/sosa/", + "qudt-1-1": "http://qudt.org/1.1/schema/qudt#", + "qudt-unit-1-1": "http://qudt.org/1.1/vocab/unit#", + "arces-monitor": "http://wot.arces.unibo.it/monitor#", + "mqtt": "http://wot.arces.unibo.it/mqtt#", + "time": "http://www.w3.org/2006/time#", + "wgs84_pos": "http://www.w3.org/2003/01/geo/wgs84_pos#", + "gn": "http://www.geonames.org/ontology#" + }, + "extended": { + "simulate": true, + "simulation" : { + "italy/site1/soilmoisture" : [35,75], + "italy/site1/pressure" : [900,1100], + "italy/site1/temperature" : [15,40] + }, + "mqtt": { + "url": "giove.arces.unibo.it", + "port": 52877, + "topics": [ + "#" + ], + "ssl": false + }, + "regexTopics": { + "pepoli:6lowpan:network": [ + ".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n", + ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n", + ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n" + ] + }, + "jsonTopics": { + "ground/lora/moisture": { + "id": "nodeId", + "value": "moistureValue" + } + }, + "semantic-mappings": { + "italy/site1/soilmoisture": { + "observation": "arces-monitor:Italy-Site1-SoilMoisture", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:ItalySite1", + "comment": "Soil moisture (Site1, Italy)", + "label": "Site 1 - Soil moisture" + }, + "italy/site1/pressure": { + "observation": "arces-monitor:Italy-Site1-Pressure", + "unit": "qudt-unit-1-1:Millibar", + "location": "arces-monitor:ItalySite1", + "comment": "Atmosferic pressure (Site1, Italy)", + "label": "Site 1 - Atmosferic pressure" + }, + "italy/site1/temperature": { + "observation": "arces-monitor:Italy-Site1-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:ItalySite1", + "comment": "Temperature (Site1, Italy)", + "label": "Site 1 - Temperature" + } + } + }, + "updates": { + "REMOVE_PLACE": { + "sparql": "DELETE WHERE {?place ?s ?p}", + "forcedBindings": { + "place": { + "type": "uri", + "value": "arces-monitor:Mars" + } + } + }, + "ADD_PLACE": { + "sparql": "INSERT DATA {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}", + "forcedBindings": { + "place": { + "type": "uri", + "value": "arces-monitor:Mars" + }, + "name": { + "type": "literal", + "value": "Mars" + }, + "lat": { + "type": "literal", + "value": "44.489664", + "datatype": "xsd:decimal" + }, + "long": { + "type": "literal", + "value": "11.357023", + "datatype": "xsd:decimal" + } + } + }, + "MQTT_MESSAGE": { + "sparql": "DELETE {?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} INSERT {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?timestamp} WHERE {OPTIONAL{?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} . BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Message-\",STRUUID())) AS ?message) . BIND(now() AS ?timestamp)}", + "forcedBindings": { + "value": { + "type": "literal", + "value": "mqttValueXYZ" + }, + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + }, + "broker": { + "type": "uri", + "value": "tcp://giove.arces.unibo.it:52887" + } + } + }, + "LOG_QUANTITY": { + "sparql": "INSERT {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value ; qudt-1-1:unit ?unit ; time:inXSDDateTimeStamp ?timestamp} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#Log-\",STRUUID())) AS ?log) . BIND(now() AS ?timestamp)}", + "graphs": { + "using-graph-uri": "http://wot.arces.unibo.it/swamp/log", + "using-named-graph-uri": "http://wot.arces.unibo.it/swamp/log" + }, + "forcedBindings": { + "quantity": { + "type": "uri", + "value": "arces-monitor:QuantityValueXYZ" + }, + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + }, + "value": { + "type": "literal", + "value": "1234" + } + } + }, + "REMOVE_OBSERVATION": { + "sparql": "DELETE WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit . ?quantity qudt-1-1:numericValue ?value}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "ADD_OBSERVATION": { + "sparql": "INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue 'NaN'} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity)}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + }, + "comment": { + "type": "literal", + "value": "This is an observation" + }, + "label": { + "type": "literal", + "value": "The observation XYZ" + }, + "location": { + "type": "uri", + "value": "arces-monitor:Mars" + }, + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + }, + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + } + } + }, + "UPDATE_OBSERVATION_VALUE": { + "sparql": "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + }, + "value": { + "type": "literal", + "datatype": "xsd:decimal", + "value": "12345.67890" + } + } + } + }, + "queries": { + "LOG_QUANTITY": { + "sparql": "SELECT * WHERE {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value; time:inXSDDateTimeStamp ?timestamp}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/swamp/log", + "named-graph-uri": "http://wot.arces.unibo.it/swamp/log" + } + }, + "PLACES": { + "sparql": "SELECT * WHERE {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}" + }, + "OBSERVATIONS_TOPICS": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}" + }, + "OBSERVATIONS": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit . OPTIONAL {?quantity qudt-1-1:numericValue ?value}}" + }, + "OBSERVATIONS_BY_LOCATION": { + "sparql": "SELECT * WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "location": { + "type": "uri", + "value": "arces-monitor:Mars" + } + } + }, + "OBSERVATIONS_BY_UNIT": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + } + } + }, + "ALL_VALUES": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "MQTT_TOPICS_COUNT": { + "sparql": "SELECT (COUNT(DISTINCT ?topic) AS ?topics) WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic}" + }, + "MQTT_TOPICS": { + "sparql": "SELECT DISTINCT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" + }, + "MQTT_TOPIC_VALUE": { + "sparql": "SELECT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", + "forcedBindings": { + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + } + } + }, + "MQTT_MESSAGES": { + "sparql": "SELECT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" + } + } +} \ No newline at end of file From f2825888e0e5141a7b896bf11c7b4402502219c5 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 29 Jun 2018 10:09:07 +0200 Subject: [PATCH 16/76] Unused import removed --- .../java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java index 00509732..34afdf82 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProperties.java @@ -18,8 +18,6 @@ package it.unibo.arces.wot.sepa.api; -import java.io.File; - import com.google.gson.JsonElement; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; From 1de780aec30ab4117ad7e305193753218028ed86 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 29 Jun 2018 10:31:41 +0200 Subject: [PATCH 17/76] Update sepatest.jsap for security --- tools/sepatest-secure.jsap | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tools/sepatest-secure.jsap b/tools/sepatest-secure.jsap index c33521be..300828aa 100644 --- a/tools/sepatest-secure.jsap +++ b/tools/sepatest-secure.jsap @@ -1,5 +1,10 @@ { "host": "localhost", + "oauth": { + "enable" : true, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, "sparql11protocol": { "protocol": "https", "port": 8443, @@ -27,15 +32,6 @@ } } }, - "authentication": { - "register": "https://localhost:8443/oauth/register", - "tokenRequest": "https://localhost:8443/oauth/token", - "client_id": "/Jl3tCgdcITFDGwvZc1uU6HIXFrzRG86TipxeMlTLHTTcDxv7Jd8WwGbDcnCsOXu", - "client_secret": "5hxu6NNu1b90apLo+enncmVqBb4TZ52TgfS9/Odv83XKz54Qn6XxcFozCPfXgkCE", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfs6S0KjyGmz2uItkCJ9H74fK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYkYWKxOC4COdNRtVu7rg5GSB/eB7UZs7f+B0WqYL4JnSCRFlsQfvdfz4qcBG54NaCBo8DVzJgepsV9VehT9P5QEe+x5IyPbGA90/889ZMzfn/8nTgaq2PSsFXnhEC/E3qocmsViba+3Fgbf/uV31v42WrAwgEYotiOauU4ovw8uXwP+jVuwPPt0DlduAiHaAZtspq00sDIJxdyLrNHnSUpSAYQ4TgE/lY850QsapHjDbziwOREkq3JIZZ23cIA03pG0NQHFmIClRlogDWwmjYIdyCk6lP5UH07HDnm+RF1FSig6VBfhRbikGJEFfYH1rEuN2eXpEeM8AnpSb+/dv9Yk/X/Dw4PQG+4KN9jZm4YmLiIuHI3dkgGdG+GMCrHH+xvMEyVd7jS5VPeLpQ9WsH/6JfgrekfTyswiEt/WVzmIs+kqov27hqNfwdeUuFqKIgtVTONLbaNoPrRZUaf3PBTfZFoDWhCRiJSeEDToBLT2n/NADkun6gRrFRLjrrfbvQVLrLrj9ExO746qi+1CQnozl25naixdbmcCRytzXX4jsmLCJRjFJm4eioUXe5jfTR8vfiEL3Gtzmaq67dtE68leMZenuuAy5MLtPTkxRACANPvvce1/g7mjmG5ZeLp5XR65q4TYyHQbKkoYFZkOhLoW", - "expires": "X1z+7ZLml+hDHC2fIp47NQ==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - }, "graphs": { "default-graph-uri": "http://sepatest", "named-graph-uri": "http://sepatest", From 4724898e34159b9163192760d0d13a4a0b9c6245 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Mon, 2 Jul 2018 12:02:57 +0200 Subject: [PATCH 18/76] Fixed a bug on dashboard and upated version of arces demo MQTT --- engine/src/main/resources/log4j2.xml | 5 +- tools/arces-demo.jsap | 726 ++++++++++++++++++ .../.gitignore | 1 + .../arces/wot/sepa/apps/mqtt/MQTTAdapter.java | 11 +- .../{MQTTInitializer.java => MQTTMapper.java} | 6 +- .../arces/wot/sepa/apps/mqtt/MQTTMonitor.java | 15 +- .../wot/sepa/apps/mqtt/MQTTSmartifier.java | 4 +- .../wot/sepa/apps/mqtt/ObservationLogger.java | 4 +- .../sepa/apps/mqtt/ObservationRemover.java | 4 +- .../unibo/arces/wot/sepa/tools/Dashboard.java | 8 +- tools/src/main/resources/log4j2.xml | 6 +- 11 files changed, 764 insertions(+), 26 deletions(-) create mode 100644 tools/arces-demo.jsap create mode 100644 tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore rename tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/{MQTTInitializer.java => MQTTMapper.java} (90%) diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index 5bdcc554..e308cae7 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -26,9 +26,8 @@ ALL Integer.MAX_VALUE - - - + + diff --git a/tools/arces-demo.jsap b/tools/arces-demo.jsap new file mode 100644 index 00000000..24171a0d --- /dev/null +++ b/tools/arces-demo.jsap @@ -0,0 +1,726 @@ +{ + "host": "mml.arces.unibo.it", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", + "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", + "using-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", + "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt" + }, + "namespaces": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "sosa": "http://www.w3.org/ns/sosa/", + "qudt-1-1": "http://qudt.org/1.1/schema/qudt#", + "qudt-unit-1-1": "http://qudt.org/1.1/vocab/unit#", + "arces-monitor": "http://wot.arces.unibo.it/monitor#", + "mqtt": "http://wot.arces.unibo.it/mqtt#", + "time": "http://www.w3.org/2006/time#", + "wgs84_pos": "http://www.w3.org/2003/01/geo/wgs84_pos#", + "gn": "http://www.geonames.org/ontology#" + }, + "extended": { + "simulate": false, + "simulation" : { + "italy/site1/soilmoisture" : [35,75], + "italy/site1/pressure" : [900,1100], + "italy/site1/temperature" : [15,40] + }, + "mqtt": { + "url": "giove.arces.unibo.it", + "port": 52877, + "topics": [ + "#" + ], + "ssl": false + }, + "regexTopics": { + "pepoli:6lowpan:network": [ + ".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n", + ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n", + ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n" + ] + }, + "jsonTopics": { + "ground/lora/moisture": { + "id": "nodeId", + "value": "moistureValue" + } + }, + "semantic-mappings": { + "/ffa574972ab9/applink/107/001BC50C700009CD": { + "observation": "arces-monitor:001BC50C700009CD-DASH7-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura sensori DASH7 sede Pepoli", + "label": "Temperatura sensore 001BC50C700009CD" + }, + "/ffa574972ab9/applink/107/001BC50C700009BB": { + "observation": "arces-monitor:001BC50C700009BB-DASH7-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura sensori DASH7 sede Pepoli", + "label": "Temperatura sensore 001BC50C700009BB" + }, + "pepoli/6lowpan/network/NODO1/Temperature": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura sala server Viale Pepoli (rete 6LowPan)", + "label": "Temperatura sala server Viale Pepoli" + }, + "pepoli/6lowpan/network/NODO1/Humidity": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità sala server Viale Pepoli (rete 6LowPan)", + "label": "Umidità sala server Viale Pepoli" + }, + "pepoli/6lowpan/network/NODO1/Pressure": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Pressure", + "unit": "qudt-unit-1-1:Millibar", + "location": "arces-monitor:Star", + "comment": "Pressione atmosferica sala server Viale Pepoli (rete 6LowPan)", + "label": "Pressione atmosferica sala server Viale Pepoli" + }, + "pepoli/6lowpan/network/NODO2/Temperature": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura ufficio Luca Perilli (rete 6LowPan)", + "label": "Temperatura ufficio Luca Perilli" + }, + "pepoli/6lowpan/network/NODO2/Humidity": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità ufficio SLuca Perilli (rete 6LowPan)", + "label": "Umidità ufficio Luca Perilli" + }, + "pepoli/6lowpan/network/NODO2/Pressure": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Pressure", + "unit": "qudt-unit-1-1:Millibar", + "location": "arces-monitor:Star", + "comment": "Pressione atmosferica ufficio SLuca Perilli (rete 6LowPan)", + "label": "Pressione atmosferica sala server Viale Pepoli" + }, + "pepoli/6lowpan/network/NODO3/Temperature": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura Blue Hall (rete 6LowPan)", + "label": "Temperatura Blue Hall" + }, + "pepoli/6lowpan/network/NODO3/Humidity": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità Blue Hall (rete 6LowPan)", + "label": "Umidità Blue Hall" + }, + "pepoli/6lowpan/network/NODO3/Pressure": { + "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Pressure", + "unit": "qudt-unit-1-1:Millibar", + "location": "arces-monitor:Star", + "comment": "Pressione atmosferica Blue Hall (rete 6LowPan)", + "label": "Pressione atmosferica Blue Hall" + }, + "ground/lora/moisture/device1": { + "observation": "arces-monitor:ground-lora-moisture-device1", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità giardino 1 (rete LoRa)", + "label": "Umidità giardino" + }, + "ground/lora/moisture/device2": { + "observation": "arces-monitor:ground-lora-moisture-device2", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità giardino 2 (rete LoRa)", + "label": "Umidità giardino" + }, + "arces/servers/ares/ercole/cpu/core-20/temperature": { + "observation": "arces-monitor:ServerErcoleCore20", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 20", + "label": "Temperatura Server ERCOLE Core 20" + }, + "arces/servers/ares/ercole/cpu/core-19/temperature": { + "observation": "arces-monitor:ServerErcoleCore19", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 19", + "label": "Temperatura Server ERCOLE Core 19" + }, + "arces/servers/ares/ercole/cpu/core-18/temperature": { + "observation": "arces-monitor:ServerErcoleCore18", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 18", + "label": "Temperatura Server ERCOLE Core 18" + }, + "arces/servers/ares/ercole/cpu/core-17/temperature": { + "observation": "arces-monitor:ServerErcoleCore17", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 17", + "label": "Temperatura Server ERCOLE Core 17" + }, + "arces/servers/ares/ercole/cpu/core-16/temperature": { + "observation": "arces-monitor:ServerErcoleCore16", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 16", + "label": "Temperatura Server ERCOLE Core 16" + }, + "arces/servers/ares/ercole/cpu/core-15/temperature": { + "observation": "arces-monitor:ServerErcoleCore15", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 15", + "label": "Temperatura Server ERCOLE Core 15" + }, + "arces/servers/ares/ercole/cpu/core-14/temperature": { + "observation": "arces-monitor:ServerErcoleCore14", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 14", + "label": "Temperatura Server ERCOLE Core 14" + }, + "arces/servers/ares/ercole/cpu/core-13/temperature": { + "observation": "arces-monitor:ServerErcoleCore13", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 13", + "label": "Temperatura Server ERCOLE Core 13" + }, + "arces/servers/ares/ercole/cpu/core-12/temperature": { + "observation": "arces-monitor:ServerErcoleCore12", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 12", + "label": "Temperatura Server ERCOLE Core 12" + }, + "arces/servers/ares/ercole/cpu/core-11/temperature": { + "observation": "arces-monitor:ServerErcoleCore11", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 11", + "label": "Temperatura Server ERCOLE Core 11" + }, + "arces/servers/ares/ercole/cpu/core-10/temperature": { + "observation": "arces-monitor:ServerErcoleCore10", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 10", + "label": "Temperatura Server ERCOLE Core 10" + }, + "arces/servers/ares/ercole/cpu/core-9/temperature": { + "observation": "arces-monitor:ServerErcoleCore9", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 9", + "label": "Temperatura Server ERCOLE Core 9" + }, + "arces/servers/ares/ercole/cpu/core-8/temperature": { + "observation": "arces-monitor:ServerErcoleCore8", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 8", + "label": "Temperatura Server ERCOLE Core 8" + }, + "arces/servers/ares/ercole/cpu/core-7/temperature": { + "observation": "arces-monitor:ServerErcoleCore7", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 7", + "label": "Temperatura Server ERCOLE Core 7" + }, + "arces/servers/ares/ercole/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerErcoleCore6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 6", + "label": "Temperatura Server ERCOLE Core 6" + }, + "arces/servers/ares/ercole/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerErcoleCore5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 5", + "label": "Temperatura Server ERCOLE Core 5" + }, + "arces/servers/ares/ercole/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerErcoleCore4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 4", + "label": "Temperatura Server ERCOLE Core 4" + }, + "arces/servers/ares/ercole/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerErcoleCore3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 3", + "label": "Temperatura Server ERCOLE Core 3" + }, + "arces/servers/ares/ercole/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerErcoleCore2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 2", + "label": "Temperatura Server ERCOLE Core 2" + }, + "arces/servers/ares/ercole/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerErcoleCore1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 1", + "label": "Temperatura Server ERCOLE Core 1" + }, + "arces/servers/mars/mml/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerMml6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 6", + "label": "Temperatura Server MML Core 6" + }, + "arces/servers/mars/mml/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerMml5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 5", + "label": "Temperatura Server MML Core 5" + }, + "arces/servers/mars/mml/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerMml4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 4", + "label": "Temperatura Server MML Core 4" + }, + "arces/servers/mars/mml/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerMml3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 3", + "label": "Temperatura Server MML Core 3" + }, + "arces/servers/mars/mml/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerMml2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 2", + "label": "Temperatura Server MML Core 2" + }, + "arces/servers/mars/mml/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerMml1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 1", + "label": "Temperatura Server MML Core 1" + }, + "arces/servers/mars/giove/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerGiove6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 6", + "label": "Temperatura Server GIOVE Core 6" + }, + "arces/servers/mars/giove/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerGiove5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 5", + "label": "Temperatura Server GIOVE Core 5" + }, + "arces/servers/mars/giove/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerGiove4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 4", + "label": "Temperatura Server GIOVE Core 4" + }, + "arces/servers/mars/giove/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerGiove3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 3", + "label": "Temperatura Server GIOVE Core 3" + }, + "arces/servers/mars/giove/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerGiove2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 2", + "label": "Temperatura Server GIOVE Core 2" + }, + "arces/servers/mars/giove/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerGiove1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 1", + "label": "Temperatura Server GIOVE Core 1" + }, + "arces/servers/mars/mml/hd/sda/temperature": { + "observation": "arces-monitor:ServerMmlHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML HDD SDA", + "label": "Temperatura Server MML HDD SDA" + }, + "arces/servers/mars/mml/hd/sdb/temperature": { + "observation": "arces-monitor:ServerMmlHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML HDD SDB", + "label": "Temperatura Server MML HDD SDB" + }, + "arces/servers/mars/giove/hd/sda/temperature": { + "observation": "arces-monitor:ServerGioveHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDA", + "label": "Temperatura Server GIOVE HDD SDA" + }, + "arces/servers/mars/giove/hd/sdb/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDB", + "label": "Temperatura Server GIOVE HDD SDB" + }, + "arces/servers/mars/giove/hd/sdc/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdc", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDC", + "label": "Temperatura Server GIOVE HDD SDC" + }, + "arces/servers/mars/giove/hd/sdd/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdd", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDD", + "label": "Temperatura Server GIOVE HDD SDD" + }, + "arces/servers/mars/giove/hd/sdf/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdf", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDF", + "label": "Temperatura Server GIOVE HDD SDF" + }, + "arces/servers/mars/giove/hd/sdg/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdg", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDG", + "label": "Temperatura Server GIOVE HDD SDG" + }, + "arces/servers/mars/giove/hd/sdh/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdh", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDH", + "label": "Temperatura Server GIOVE HDD SDH" + }, + "arces/servers/mars/marsamba/hd/sda/temperature": { + "observation": "arces-monitor:ServerSambaHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server SAMBA HDD SDA", + "label": "Temperatura Server SAMBA HDD SDA" + }, + "arces/servers/mars/marsamba/hd/sdb/temperature": { + "observation": "arces-monitor:ServerSambaHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server SAMBA HDD SDB", + "label": "Temperatura Server SAMBA HDD SDB" + }, + "arces/servers/ares/ercole/hd/sda/temperature": { + "observation": "arces-monitor:ServerErcoleHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE HDD SDA", + "label": "Temperatura Server ERCOLE HDD SDA" + }, + "arces/servers/ares/ercole/hd/sdb/temperature": { + "observation": "arces-monitor:ServerErcoleHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE HDD SDB", + "label": "Temperatura Server ERCOLE HDD SDB" + }, + "5CCF7F15676D/temperature": { + "observation": "arces-monitor:5CCF7F15676D-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura zona rack sala server Toffano", + "label": "Temperatura zona rack" + }, + "5CCF7F15676D/humidity": { + "observation": "arces-monitor:5CCF7F15676D-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Mars", + "comment": "Umidità zona rack sala server Toffano", + "label": "Umidità zona rack" + }, + "5CCF7F1B599E/temperature": { + "observation": "arces-monitor:5CCF7F1B599E-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura zona finestra sala server Toffano", + "label": "Temperatura zona finestra" + }, + "5CCF7F1B599E/humidity": { + "observation": "arces-monitor:5CCF7F1B599E-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Mars", + "comment": "Umidità zona finestra sala server Toffano", + "label": "Umidità zona finestra" + }, + "5CCF7F151DC9/temperature": { + "observation": "arces-monitor:5CCF7F151DC9-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura esterna sala server Toffano", + "label": "Temperatura esterna" + }, + "5CCF7F1B58AC/temperature": { + "observation": "arces-monitor:5CCF7F1B58AC-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura zona rack sala server Pepoli", + "label": "Temperatura zona rack" + }, + "5CCF7F1B58AC/humidity": { + "observation": "arces-monitor:5CCF7F1B58AC-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità zona rack sala server Pepoli", + "label": "Umidità zona rack" + } + } + }, + "updates": { + "REMOVE_PLACE": { + "sparql": "DELETE WHERE {?place ?s ?p}", + "forcedBindings": { + "place": { + "type": "uri", + "value": "arces-monitor:Mars" + } + } + }, + "ADD_PLACE": { + "sparql": "INSERT DATA {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}", + "forcedBindings": { + "place": { + "type": "uri", + "value": "arces-monitor:Mars" + }, + "name": { + "type": "literal", + "value": "Mars" + }, + "lat": { + "type": "literal", + "value": "44.489664", + "datatype": "xsd:decimal" + }, + "long": { + "type": "literal", + "value": "11.357023", + "datatype": "xsd:decimal" + } + } + }, + "MQTT_MESSAGE": { + "sparql": "DELETE {?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} INSERT {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?timestamp} WHERE {OPTIONAL{?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} . BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Message-\",STRUUID())) AS ?message) . BIND(now() AS ?timestamp)}", + "forcedBindings": { + "value": { + "type": "literal", + "value": "mqttValueXYZ" + }, + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + }, + "broker": { + "type": "uri", + "value": "tcp://giove.arces.unibo.it:52887" + } + } + }, + "LOG_QUANTITY": { + "sparql": "INSERT {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value ; qudt-1-1:unit ?unit ; time:inXSDDateTimeStamp ?timestamp} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#Log-\",STRUUID())) AS ?log) . BIND(now() AS ?timestamp)}", + "graphs": { + "using-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log", + "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log" + }, + "forcedBindings": { + "quantity": { + "type": "uri", + "value": "arces-monitor:QuantityValueXYZ" + }, + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + }, + "value": { + "type": "literal", + "value": "1234" + } + } + }, + "REMOVE_OBSERVATION": { + "sparql": "DELETE WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit . ?quantity qudt-1-1:numericValue ?value}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "ADD_OBSERVATION": { + "sparql": "INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue 'NaN'} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity)}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + }, + "comment": { + "type": "literal", + "value": "This is an observation" + }, + "label": { + "type": "literal", + "value": "The observation XYZ" + }, + "location": { + "type": "uri", + "value": "arces-monitor:Mars" + }, + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + }, + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + } + } + }, + "UPDATE_OBSERVATION_VALUE": { + "sparql": "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + }, + "value": { + "type": "literal", + "datatype": "xsd:decimal", + "value": "12345.67890" + } + } + } + }, + "queries": { + "LOG_QUANTITY": { + "sparql": "SELECT * WHERE {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value; time:inXSDDateTimeStamp ?timestamp}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log", + "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log" + } + }, + "PLACES": { + "sparql": "SELECT * WHERE {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}" + }, + "OBSERVATIONS_TOPICS": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}" + }, + "OBSERVATIONS": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit . OPTIONAL {?quantity qudt-1-1:numericValue ?value}}" + }, + "OBSERVATIONS_BY_LOCATION": { + "sparql": "SELECT * WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "location": { + "type": "uri", + "value": "arces-monitor:Mars" + } + } + }, + "OBSERVATIONS_BY_UNIT": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + } + } + }, + "ALL_VALUES": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "MQTT_TOPICS_COUNT": { + "sparql": "SELECT (COUNT(DISTINCT ?topic) AS ?topics) WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic}" + }, + "MQTT_TOPICS": { + "sparql": "SELECT DISTINCT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" + }, + "MQTT_TOPIC_VALUE": { + "sparql": "SELECT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", + "forcedBindings": { + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + } + } + }, + "MQTT_MESSAGES": { + "sparql": "SELECT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" + } + } +} \ No newline at end of file diff --git a/tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore b/tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore new file mode 100644 index 00000000..38ec9779 --- /dev/null +++ b/tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore @@ -0,0 +1 @@ +/.lck diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java index 9dbd82f1..1858b57c 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java @@ -33,7 +33,12 @@ public class MQTTAdapter extends Producer implements MqttCallback { private String serverURI = null; public static void main(String[] args) throws IOException, SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - MQTTAdapter adapter = new MQTTAdapter(); + if (args.length != 1) { + logger.error("Please provide the jsap file as argument"); + System.exit(-1); + } + + MQTTAdapter adapter = new MQTTAdapter(args[0]); adapter.start(); System.out.println("Press any key to exit..."); @@ -43,8 +48,8 @@ public static void main(String[] args) throws IOException, SEPAProtocolException adapter.close(); } - public MQTTAdapter() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new JSAP("swamp-demo.jsap"), "MQTT_MESSAGE"); + public MQTTAdapter(String jsap) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + super(new JSAP(jsap), "MQTT_MESSAGE"); } @Override diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java similarity index 90% rename from tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java rename to tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java index 63e650fb..ae26faff 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTInitializer.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java @@ -17,11 +17,11 @@ import it.unibo.arces.wot.sepa.pattern.JSAP; import it.unibo.arces.wot.sepa.pattern.Producer; -public class MQTTInitializer extends Producer { +public class MQTTMapper extends Producer { private static final Logger logger = LogManager.getLogger(); - public MQTTInitializer() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, IOException { - super(new JSAP("swamp-demo.jsap"), "ADD_OBSERVATION"); + public MQTTMapper(String jsap) throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, IOException { + super(new JSAP(jsap), "ADD_OBSERVATION"); } public void init() throws SEPASecurityException, IOException, SEPAPropertiesException { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java index c52e0153..d53be2e3 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java @@ -16,26 +16,31 @@ public class MQTTMonitor { private static MQTTSmartifier smartifier; // Add observation based on the semantic mapping stored in JSAP - private static MQTTInitializer mqttInitializer; + private static MQTTMapper mqttInitializer; public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException { + if (args.length != 1) { + logger.error("Please provide the jsap file as argument"); + System.exit(-1); + } + // Logger - ObservationLogger analytics = new ObservationLogger(); + ObservationLogger analytics = new ObservationLogger(args[0]); analytics.subscribe(); // Remover - ObservationRemover remover = new ObservationRemover(); + ObservationRemover remover = new ObservationRemover(args[0]); remover.removeAll(); remover.close(); // Inizializer - mqttInitializer = new MQTTInitializer(); + mqttInitializer = new MQTTMapper(args[0]); mqttInitializer.init(); mqttInitializer.close(); // Create MQTT smartifier - smartifier = new MQTTSmartifier(); + smartifier = new MQTTSmartifier(args[0]); if (smartifier.start()) { logger.info("Press any key to exit..."); try { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java index 67455af0..b845e26b 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java @@ -45,8 +45,8 @@ public class MQTTSmartifier extends Aggregator implements MqttCallback { // Topics mapping private HashMap topic2observation = new HashMap(); - public MQTTSmartifier() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new JSAP("swamp-demo.jsap"), "OBSERVATIONS_TOPICS", "UPDATE_OBSERVATION_VALUE"); + public MQTTSmartifier(String jsap) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + super(new JSAP(jsap), "OBSERVATIONS_TOPICS", "UPDATE_OBSERVATION_VALUE"); } @Override diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java index 878206a5..5a121d51 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java @@ -16,9 +16,9 @@ public class ObservationLogger extends Aggregator { - public ObservationLogger() + public ObservationLogger(String jsap) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new JSAP("swamp-demo.jsap"), "OBSERVATIONS", "LOG_QUANTITY"); + super(new JSAP(jsap), "OBSERVATIONS", "LOG_QUANTITY"); } @Override diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationRemover.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationRemover.java index 17878f3b..22029d2f 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationRemover.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationRemover.java @@ -14,8 +14,8 @@ public class ObservationRemover extends Producer { - public ObservationRemover() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - super(new JSAP("swamp-demo.jsap"), "REMOVE_OBSERVATION"); + public ObservationRemover(String jsap) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + super(new JSAP(jsap), "REMOVE_OBSERVATION"); } public void remove(String observation) throws SEPASecurityException, IOException, SEPAPropertiesException { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index 66e3c44f..c62cf901 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -1581,8 +1581,10 @@ protected void subscribe() throws IOException, SEPAPropertiesException { String type = queryForcedBindings.getValueAt(row, 2).toString(); String value = queryForcedBindings.getValueAt(row, 1).toString(); String variable = queryForcedBindings.getValueAt(row, 0).toString(); - if (type.equals("xsd:anyURI")) + if (type.toUpperCase().equals("URI")) bindings.addBinding(variable, new RDFTermURI(value)); + else if (type.toUpperCase().equals("BNODE")) + bindings.addBinding(variable, new RDFTermBNode(value)); else bindings.addBinding(variable, new RDFTermLiteral(value, type)); } @@ -1686,9 +1688,9 @@ protected void query() throws SEPAPropertiesException { String type = queryForcedBindings.getValueAt(row, 2).toString(); String value = queryForcedBindings.getValueAt(row, 1).toString(); String variable = queryForcedBindings.getValueAt(row, 0).toString(); - if (type.equals("URI")) + if (type.toUpperCase().equals("URI")) bindings.addBinding(variable, new RDFTermURI(value)); - else if (type.equals("BNODE")) + else if (type.toUpperCase().equals("BNODE")) bindings.addBinding(variable, new RDFTermBNode(value)); else bindings.addBinding(variable, new RDFTermLiteral(value, type)); diff --git a/tools/src/main/resources/log4j2.xml b/tools/src/main/resources/log4j2.xml index ab42153c..77af7ae9 100644 --- a/tools/src/main/resources/log4j2.xml +++ b/tools/src/main/resources/log4j2.xml @@ -12,7 +12,7 @@ DEBUG 500 TRACE 600 ALL Integer.MAX_VALUE --> - + @@ -23,8 +23,8 @@ ALL Integer.MAX_VALUE - - + + From 8be3d2c70e7a2bada83c1294d390d95ee690238e Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Tue, 3 Jul 2018 10:35:22 +0200 Subject: [PATCH 19/76] Changed labels of sensors --- tools/arces-demo.jsap | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/arces-demo.jsap b/tools/arces-demo.jsap index 24171a0d..b743ff43 100644 --- a/tools/arces-demo.jsap +++ b/tools/arces-demo.jsap @@ -83,15 +83,15 @@ "observation": "arces-monitor:001BC50C700009CD-DASH7-Temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Star", - "comment": "Temperatura sensori DASH7 sede Pepoli", - "label": "Temperatura sensore 001BC50C700009CD" + "comment": "Temperature sensor used to show the need of a new air conditioning system in SEHM lab", + "label": "Temperature SEHM lab" }, "/ffa574972ab9/applink/107/001BC50C700009BB": { "observation": "arces-monitor:001BC50C700009BB-DASH7-Temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Star", - "comment": "Temperatura sensori DASH7 sede Pepoli", - "label": "Temperatura sensore 001BC50C700009BB" + "comment": "emperature sensor used to show the need of a new air conditioning system in ST office", + "label": "Temperature ST office" }, "pepoli/6lowpan/network/NODO1/Temperature": { "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature", From 168508a5320d3609b754855f0ec2d2f2815c9db6 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Tue, 3 Jul 2018 16:31:14 +0200 Subject: [PATCH 20/76] Working on JUnit tests Fixed minor bugs in SEPATestClient application --- .../unibo/arces/wot/sepa/pattern/Client.java | 3 - .../arces/wot/sepa/pattern/GenericClient.java | 5 +- .../wot/sepa/api/ConfigurationProvider.java | 2 +- .../arces/wot/sepa/api/ITProtocolTest.java | 216 +++++++++++------- .../src/test/resources/sepatest-secure.jsap | 123 ++++++++++ client-api/src/test/resources/sepatest.jsap | 113 +++++++++ tools/sepatest-secure.jsap | 124 +--------- .../arces/wot/sepa/tools/SEPATestClient.java | 52 ++--- 8 files changed, 393 insertions(+), 245 deletions(-) create mode 100644 client-api/src/test/resources/sepatest-secure.jsap create mode 100644 client-api/src/test/resources/sepatest.jsap diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java index f4e1a464..7417736e 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java @@ -74,8 +74,6 @@ public Client(JSAP appProfile) throws SEPAProtocolException { logger.trace("SEPA parameters: " + appProfile.printParameters()); addNamespaces(appProfile); - - if (appProfile.isSecure()) throw new IllegalArgumentException("Wrong construct. Security is enabled but security manager is null."); isSecure = appProfile.isSecure(); @@ -96,7 +94,6 @@ public Client(JSAP appProfile,SEPASecurityManager sm) throws SEPAProtocolExcepti if (sm == null & appProfile.isSecure()) throw new IllegalArgumentException("Security is enabled but security manager is null"); this.sm = sm; - isSecure = appProfile.isSecure(); } protected final String replaceBindings(String sparql, Bindings bindings) { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java index a88fde6b..e1e8c2cc 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java @@ -52,10 +52,7 @@ public GenericClient(JSAP appProfile) throws SEPAProtocolException { } public GenericClient(JSAP appProfile, SEPASecurityManager sm) throws SEPAProtocolException { - super(appProfile); - if (sm == null) - throw new IllegalArgumentException("Security manager is null"); - this.sm = sm; + super(appProfile,sm); } public Response update(String ID, String sparql, Bindings forced, int timeout) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index e3d06e47..03a8ffa9 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -14,7 +14,7 @@ public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEP if( configuaration != null){ result = new JSAP(configuaration); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("dev.jsap"); + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); result = new JSAP(config.getPath()); } return result; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java index 8c302e7b..9a82bbf1 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java @@ -1,12 +1,10 @@ package it.unibo.arces.wot.sepa.api; -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.pattern.JSAP; import org.junit.After; @@ -14,98 +12,142 @@ import org.junit.Test; import java.io.IOException; -import java.util.List; import static org.junit.Assert.*; public class ITProtocolTest { - private SPARQL11SEProtocol client; - private MockSubscriptionHandler subHandler; - private JSAP properties = null; - - @Before - public void setUp() throws Exception { - properties = ConfigurationProvider.GetTestEnvConfiguration(); - subHandler = new MockSubscriptionHandler(); - - ISubscriptionProtocol protocol = null; - protocol = new WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), - properties.getSubscribePort(null), properties.getSubscribePath(null)); - - client = new SPARQL11SEProtocol(protocol, subHandler); + // private SPARQL11SEProtocol client; + // private MockSubscriptionHandler subHandler; + private static JSAP properties = null; + + @Before + public void setUp() throws Exception { + properties = ConfigurationProvider.GetTestEnvConfiguration(); + // subHandler = new MockSubscriptionHandler(); + // + // ISubscriptionProtocol protocol = null; + // protocol = new + // WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), + // properties.getSubscribePort(null), properties.getSubscribePath(null)); + // + // client = new SPARQL11SEProtocol(protocol, subHandler); + } + + @After + public void tearDown() throws IOException { + // if(client != null) { + // client.close(); + // } + } + + @Test(timeout=5000) + public void UpdateTest() throws IOException{ + SPARQL11Protocol client = new SPARQL11Protocol(); + UpdateRequest req = getUpdateRequest("U1",5000,null); + final Response ret = client.update(req); + client.close(); + assertFalse(String.valueOf(ret),ret.isError()); } - - @After - public void tearDown() throws IOException { - if(client != null) { - client.close(); - } - } - - @Test(timeout=5000) - public void Update(){ - final Response update = SubmitUpdate(client,TestQueries.SIMPLE_UPDATE); - assertFalse(String.valueOf(update),update.isError()); - } - - @Test(timeout=5000) - public void Query(){ - final Response response = SubmitQuery(client, TestQueries.SIMPLE_QUERY); - assertFalse(String.valueOf(response),response.isError()); - } - - @Test(timeout=5000) - public void Subscribe(){ - final Response response = submitSubscribe(TestQueries.SIMPLE_QUERY, client); - assertFalse(String.valueOf(response),response.isError()); - } - - @Test(timeout=20000) - public void NotificationTest() throws InterruptedException { - final Response response = submitSubscribe(TestQueries.NOTIF_QUERY, client); - assertFalse(String.valueOf(response),response.isError()); - - final Response update = SubmitUpdate(client,TestQueries.NOTIF_UPDATE); - assertFalse(String.valueOf(update),update.isError()); - - final Response notification = subHandler.getResponse(); - assertFalse(String.valueOf(notification),notification.isError()); - - + + @Test(timeout=5000) + public void QueryTest() throws IOException{ + SPARQL11Protocol client = new SPARQL11Protocol(); + QueryRequest req = getQueryRequest("Q1",5000,null); + final Response ret = client.query(req); + client.close(); + assertFalse(String.valueOf(ret),ret.isError()); } - @Test - public void VerifiedUTF8Update(){ - Update(); - - final Response response = SubmitQuery(client, TestQueries.UTF8_RESULT_QUERY); - assertFalse(String.valueOf(response),response.isError()); - - QueryResponse queryResponse = (QueryResponse) response; - List results = queryResponse.getBindingsResults().getBindings(); - assertTrue("Query results empty",results.size() > 0 ); - - Bindings bindings = results.get(0); - assertTrue("Binding variable is not a literal",bindings.isLiteral("o")); - - String value = bindings.getValue("o"); - assertEquals("Incorrect utf-8 value","測試",value); + private static UpdateRequest getUpdateRequest(String id,int timeout,String authorization) { + HTTPMethod method = properties.getUpdateMethod(id); + String scheme = properties.getUpdateProtocolScheme(id); + String host = properties.getUpdateHost(id); + int port = properties.getUpdatePort(id); + String path = properties.getUpdatePath(id); + String sparql = properties.getSPARQLUpdate(id); + String graphUri = properties.getUsingGraphURI(id); + String namedGraphUri = properties.getUsingNamedGraphURI(id); + + return new UpdateRequest( method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, authorization); } - - private static Response SubmitQuery(SPARQL11SEProtocol client,String query) { - final QueryRequest queryRequest = new QueryRequest(query); - - return client.query(queryRequest); + + private static QueryRequest getQueryRequest(String id,int timeout,String authorization) { + HTTPMethod method = properties.getQueryMethod(id); + String scheme = properties.getQueryProtocolScheme(id); + String host = properties.getQueryHost(id); + int port = properties.getQueryPort(id); + String path = properties.getQueryPath(id); + String sparql = properties.getSPARQLQuery(id); + String graphUri = properties.getDefaultGraphURI(id); + String namedGraphUri = properties.getNamedGraphURI(id); + + return new QueryRequest( method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, authorization); } - private static Response SubmitUpdate(SPARQL11SEProtocol client,String query) { - final UpdateRequest updateRequest = new UpdateRequest(query); - return client.update(updateRequest); - } - - private static Response submitSubscribe(String query, SPARQL11SEProtocol client) { - SubscribeRequest sub = new SubscribeRequest(query,null,null,null,null); - return client.subscribe(sub); - } + // @Test(timeout=5000) + // public void Update(){ + // final Response update = SubmitUpdate(client,TestQueries.SIMPLE_UPDATE); + // assertFalse(String.valueOf(update),update.isError()); + // } + // + // @Test(timeout=5000) + // public void Query(){ + // final Response response = SubmitQuery(client, TestQueries.SIMPLE_QUERY); + // assertFalse(String.valueOf(response),response.isError()); + // } + // + // @Test(timeout=5000) + // public void Subscribe(){ + // final Response response = submitSubscribe(TestQueries.SIMPLE_QUERY, client); + // assertFalse(String.valueOf(response),response.isError()); + // } + // + // @Test(timeout=20000) + // public void NotificationTest() throws InterruptedException { + // final Response response = submitSubscribe(TestQueries.NOTIF_QUERY, client); + // assertFalse(String.valueOf(response),response.isError()); + // + // final Response update = SubmitUpdate(client,TestQueries.NOTIF_UPDATE); + // assertFalse(String.valueOf(update),update.isError()); + // + // final Response notification = subHandler.getResponse(); + // assertFalse(String.valueOf(notification),notification.isError()); + // } + // + // @Test + // public void VerifiedUTF8Update(){ + // Update(); + // + // final Response response = SubmitQuery(client, TestQueries.UTF8_RESULT_QUERY); + // assertFalse(String.valueOf(response),response.isError()); + // + // QueryResponse queryResponse = (QueryResponse) response; + // List results = queryResponse.getBindingsResults().getBindings(); + // assertTrue("Query results empty",results.size() > 0 ); + // + // Bindings bindings = results.get(0); + // assertTrue("Binding variable is not a literal",bindings.isLiteral("o")); + // + // String value = bindings.getValue("o"); + // assertEquals("Incorrect utf-8 value","測試",value); + // } + // + // private static Response SubmitQuery(SPARQL11SEProtocol client,String query) { + // final QueryRequest queryRequest = new QueryRequest(query); + // + // return client.query(queryRequest); + // } + // + // private static Response SubmitUpdate(SPARQL11SEProtocol client,String query) + // { + // final UpdateRequest updateRequest = new UpdateRequest(query); + // return client.update(updateRequest); + // } + // + // private static Response submitSubscribe(String query, SPARQL11SEProtocol + // client) { + // SubscribeRequest sub = new SubscribeRequest(query,null,null,null,null); + // return client.subscribe(sub); + // } } \ No newline at end of file diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap new file mode 100644 index 00000000..300828aa --- /dev/null +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -0,0 +1,123 @@ +{ + "host": "localhost", + "oauth": { + "enable" : true, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "https", + "port": 8443, + "query": { + "path": "/secure/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/secure/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "wss", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://sepatest", + "named-graph-uri": "http://sepatest", + "using-graph-uri": "http://sepatest", + "using-named-graph-uri": "http://sepatest" + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "extended": { + "notificationMaxDelay": 2000, + "sequence": [ + { + "id": "RegisterMePlease!", + "name": "Register not allowed", + "type": "REGISTER", + "passed": false + }, + { + "id": "SEPATest", + "name": "Register allowed", + "type": "REGISTER", + "passed": true + }, + { + "name": "Update (secure)", + "type": "UPDATE", + "id": "U1", + "passed": true + }, + { + "name": "Query (secure)", + "type": "QUERY", + "id": "Q1", + "results": 1, + "passed": true + }, + { + "name": "Subscribe (secure)", + "type": "SUBSCRIBE", + "id": "Q1", + "results": 1, + "passed": true + }, + { + "name": "Update (secure)", + "type": "UPDATE", + "id": "U2", + "secure": true, + "passed": true + }, + { + "name": "Notification", + "type": "WAIT_NOTIFICATION", + "passed": true + }, + { + "name": "Unsubscribe (secure)", + "type": "UNSUBSCRIBE", + "passed": true + }, + { + "name": "Update (secure)", + "type": "UPDATE", + "id": "U2", + "passed": true + }, + { + "name": "Notification (not received)", + "type": "WAIT_NOTIFICATION", + "passed": false + } + ] + }, + "updates": { + "U1": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" + }, + "U2": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}" + } + }, + "queries": { + "Q1": { + "sparql": "select * where {?x ?y \"ვაიმეე\"}" + } + } +} \ No newline at end of file diff --git a/client-api/src/test/resources/sepatest.jsap b/client-api/src/test/resources/sepatest.jsap new file mode 100644 index 00000000..011cd293 --- /dev/null +++ b/client-api/src/test/resources/sepatest.jsap @@ -0,0 +1,113 @@ +{ + "host": "localhost", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://sepatest", + "named-graph-uri": "http://sepatest", + "using-graph-uri": "http://sepatest", + "using-named-graph-uri": "http://sepatest" + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "extended": { + "notificationMaxDelay": 2000, + "sequence": [ + { + "name": "Update", + "type": "UPDATE", + "id": "U1", + "passed": true + }, + { + "name": "Query", + "type": "QUERY", + "id": "Q1", + "results": 1, + "passed": true + }, + { + "name": "Subscribe", + "type": "SUBSCRIBE", + "id": "Q1", + "results": 1, + "passed": true + }, + { + "name": "Update", + "type": "UPDATE", + "id": "U2", + "passed": true + }, + { + "name": "Notification", + "type": "WAIT_NOTIFICATION", + "passed": true + }, + { + "name": "Unsubscribe", + "type": "UNSUBSCRIBE", + "passed": true + }, + { + "name": "Update", + "type": "UPDATE", + "id": "U2", + "passed": true + }, + { + "name": "Notification (not received)", + "type": "WAIT_NOTIFICATION", + "passed": false + } + ] + }, + "updates": { + "U1": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" + }, + "U2": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}" + } + }, + "queries": { + "Q1": { + "sparql": "select * where {?x ?y \"ვაიმეე\"}" + }, + "Q2": { + "sparql": "select * where {?x ?y ?z}" + } + } +} diff --git a/tools/sepatest-secure.jsap b/tools/sepatest-secure.jsap index 300828aa..df620a28 100644 --- a/tools/sepatest-secure.jsap +++ b/tools/sepatest-secure.jsap @@ -1,123 +1 @@ -{ - "host": "localhost", - "oauth": { - "enable" : true, - "register": "https://localhost:8443/oauth/register", - "tokenRequest": "https://localhost:8443/oauth/token" - }, - "sparql11protocol": { - "protocol": "https", - "port": 8443, - "query": { - "path": "/secure/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/secure/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "wss", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/secure/subscribe" - } - } - }, - "graphs": { - "default-graph-uri": "http://sepatest", - "named-graph-uri": "http://sepatest", - "using-graph-uri": "http://sepatest", - "using-named-graph-uri": "http://sepatest" - }, - "namespaces": { - "sepa": "http://wot.arces.unibo.it/sepa#", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - }, - "extended": { - "notificationMaxDelay": 2000, - "sequence": [ - { - "id": "RegisterMePlease!", - "name": "Register not allowed", - "type": "REGISTER", - "passed": false - }, - { - "id": "SEPATest", - "name": "Register allowed", - "type": "REGISTER", - "passed": true - }, - { - "name": "Update (secure)", - "type": "UPDATE", - "id": "U1", - "passed": true - }, - { - "name": "Query (secure)", - "type": "QUERY", - "id": "Q1", - "results": 1, - "passed": true - }, - { - "name": "Subscribe (secure)", - "type": "SUBSCRIBE", - "id": "Q1", - "results": 1, - "passed": true - }, - { - "name": "Update (secure)", - "type": "UPDATE", - "id": "U2", - "secure": true, - "passed": true - }, - { - "name": "Notification", - "type": "WAIT_NOTIFICATION", - "passed": true - }, - { - "name": "Unsubscribe (secure)", - "type": "UNSUBSCRIBE", - "passed": true - }, - { - "name": "Update (secure)", - "type": "UPDATE", - "id": "U2", - "passed": true - }, - { - "name": "Notification (not received)", - "type": "WAIT_NOTIFICATION", - "passed": false - } - ] - }, - "updates": { - "U1": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" - }, - "U2": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}" - } - }, - "queries": { - "Q1": { - "sparql": "select * where {?x ?y \"ვაიმეე\"}" - } - } -} \ No newline at end of file +{"host":"localhost","oauth":{"enable":true,"register":"https://localhost:8443/oauth/register","tokenRequest":"https://localhost:8443/oauth/token","client_id":"mzSHL2SAGTXRp51RyEk0hRFSv6AR5oKigsKC3DwnIynX0WukUtEqLzWXc3Sl5/Xm","client_secret":"iGmr/pjQZwDBrQVpr85vRZvctdkHMI+2O3Y6+WaCQ2nZHIFWxXDnO7zi+ERzlDHi","jwt":"xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvZ8mpdZeckpOfsYdzUvDccK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmf04Bp3dhMHflPzxQGiDGsAuxUVGk6vpjI2Ul0ZpjebHGKBkk1w7+7kq8PPWzx5JJ+/M2BYm0FB1wJSDxOc2Jl49RWWhiACu+NmsWR4F8GotoIj4Ny+hZvM0UsSGt4iixXMF72oqV14eJdnf/VnW83vuEnWxCMKLgR6jMa3QW+/p8DJ7dchxaQ2bqhMAlkzrF3ysqfi875lcdLEfxTcyY0nuve7GDICNVT3yDRTZJdkSEuKYx8bAG7VpjAWo2fiIulDo/5i5YAK/uWKbQ8eYeVQYqVwYeYVUeegm4spqq6cLHwZYTIHo4n9P9rpN9u844Nq4KdMsQ2xGO4t0UgWGx5ciqRY5gS3+hF0DW3N2gaN5kfb+lt2XSkcAn1lb6uRFXYBszul5bT8hyDE+NKolX7X16+pOu8CZsdH6t9CXhJSCDSjliTw0Zl0430tX1qzbKakrfcvU99mw6T6T2cSWbZUgocVi80noBggXVgZxwDtyWGFc5eMoKGgx6TavrgAmIQwgP6ZkqULvnYYu5PW8iDuExfaeur/uf3M/L+l+ecwyEkprfYjGBY1LvGNnNZ9ANQel6HyIrssndjLF37Z9dIg/gDTgMujK8meJw9xigTyrqE/MUCal6SK2YgjDtPuOFu3wP2h1k1lcrHd7biUSU2","expires":"AZj3wOp7cD2iFgMUVGVfmA==","type":"XPrHEX2xHy+5IuXHPHigMw=="},"sparql11protocol":{"protocol":"https","port":8443,"query":{"path":"/secure/query","method":"POST","format":"JSON"},"update":{"path":"/secure/update","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"wss","availableProtocols":{"ws":{"port":9000,"path":"/subscribe"},"wss":{"port":9443,"path":"/secure/subscribe"}}},"graphs":{"default-graph-uri":"http://sepatest","named-graph-uri":"http://sepatest","using-graph-uri":"http://sepatest","using-named-graph-uri":"http://sepatest"},"namespaces":{"sepa":"http://wot.arces.unibo.it/sepa#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},"extended":{"notificationMaxDelay":2000,"sequence":[{"id":"RegisterMePlease!","name":"Register not allowed","type":"REGISTER","passed":false},{"id":"SEPATest","name":"Register allowed","type":"REGISTER","passed":true},{"name":"Update (secure)","type":"UPDATE","id":"U1","passed":true},{"name":"Query (secure)","type":"QUERY","id":"Q1","results":1,"passed":true},{"name":"Subscribe (secure)","type":"SUBSCRIBE","id":"Q1","results":1,"passed":true},{"name":"Update (secure)","type":"UPDATE","id":"U2","secure":true,"passed":true},{"name":"Notification","type":"WAIT_NOTIFICATION","passed":true},{"name":"Unsubscribe (secure)","type":"UNSUBSCRIBE","passed":true},{"name":"Update (secure)","type":"UPDATE","id":"U2","passed":true},{"name":"Notification (not received)","type":"WAIT_NOTIFICATION","passed":false}]},"updates":{"U1":{"sparql":"delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}"},"U2":{"sparql":"delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}"}},"queries":{"Q1":{"sparql":"select * where {?x ?y \"ვაიმეე\"}"}}} \ No newline at end of file diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java index 39ae5078..05ab4287 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Scanner; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -83,6 +82,15 @@ public SEPATestClient(JSAP appProfile) throws SEPAProtocolException, SEPASecurit this.appProfile = appProfile; } + + public SEPATestClient(JSAP appProfile,SEPASecurityManager sm) throws SEPAProtocolException, SEPASecurityException { + client = new GenericClient(appProfile,sm); + + sequence = appProfile.getExtendedData().getAsJsonObject().get("sequence").getAsJsonArray(); + notificationMaxDelay = appProfile.getExtendedData().getAsJsonObject().get("notificationMaxDelay").getAsLong(); + + this.appProfile = appProfile; + } class Results { private long failed; @@ -346,38 +354,28 @@ public void run() throws SEPASecurityException, IOException, SEPAPropertiesExcep results.print(); } - public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException { + public static void main(String[] args) throws SEPASecurityException, SEPAProtocolException, SEPAPropertiesException, IOException { System.out.println("**********************************************************"); System.out.println("*** SPARQL 1.1 SE Protocol Service test suite ***"); System.out.println("**********************************************************"); - System.out.println("*** WARNING: the RDF store content will be ERASED ***"); - System.out.println("*** Do you want to continue (yes/no)? ***"); - System.out.println("**********************************************************"); - Scanner scanner = new Scanner(System.in); - scanner.useDelimiter("\\n"); // "\\z" means end of input - String input = scanner.next(); - scanner.close(); - if (!input.equals("yes")) { - System.out.println("Bye bye! :-)"); - System.exit(0); + + SEPASecurityManager sm = null; + JSAP jsap = null; + + if (args.length != 1) { + System.err.println("Usage: SEPATestClient "); + System.exit(-1); } -// System.out.println("**********************************************************"); -// System.out.println("*** Are you sure (yes/no)? ***"); -// System.out.println("**********************************************************"); -// input = scanner.next(); -// if (!input.equals("yes")) { -// scanner.close(); -// System.out.println("Bye bye! :-)"); -// System.exit(0); -// } -// scanner.close(); - SEPATestClient test = new SEPATestClient(new JSAP("sepatest-secure.jsap")); - test.run(); + jsap = new JSAP(args[0]); - //test = new SEPATestClient(new ApplicationProfile("sepatest-secure.jsap")); - //test.run(); + SEPATestClient test = null; + if (jsap.isSecure()) { + sm = new SEPASecurityManager(); + test = new SEPATestClient(jsap,sm); + } + else test = new SEPATestClient(jsap); - System.exit(0); + test.run(); } } From 3ba521adc83b8eac3a49e28f9bbb08c1c4fcdc87 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Tue, 3 Jul 2018 17:43:01 +0200 Subject: [PATCH 21/76] JUnit Integration test wip --- .../arces/wot/sepa/api/ITProtocolTest.java | 224 +++++++++--------- .../wot/sepa/api/MockSubscriptionHandler.java | 22 +- client-api/src/test/resources/sepatest.jsap | 3 + 3 files changed, 123 insertions(+), 126 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java index 9a82bbf1..72777da7 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java @@ -1,65 +1,125 @@ package it.unibo.arces.wot.sepa.api; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.pattern.JSAP; import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.io.IOException; +import java.util.HashMap; import static org.junit.Assert.*; public class ITProtocolTest { - // private SPARQL11SEProtocol client; - // private MockSubscriptionHandler subHandler; + private static HashMap updates = new HashMap(); + private static HashMap queries = new HashMap(); + private static HashMap subscribes = new HashMap(); + private static HashMap protocols = new HashMap(); + private static JSAP properties = null; - @Before - public void setUp() throws Exception { + private static final MockSubscriptionHandler subHandler = new MockSubscriptionHandler();; + + private static SPARQL11Protocol client = null; + + @BeforeClass + public static void start() throws Exception { properties = ConfigurationProvider.GetTestEnvConfiguration(); - // subHandler = new MockSubscriptionHandler(); - // - // ISubscriptionProtocol protocol = null; - // protocol = new - // WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), - // properties.getSubscribePort(null), properties.getSubscribePath(null)); - // - // client = new SPARQL11SEProtocol(protocol, subHandler); + + for (String id : properties.getQueryIds()) { + queries.put(id, getQueryRequest(id, 5000, null)); + subscribes.put(id, getSubscribeRequest(id, null)); + protocols.put(id, new WebSocketSubscriptionProtocol(properties.getSubscribeHost(id), + properties.getSubscribePort(id), properties.getSubscribePath(id))); + } + + for (String id : properties.getUpdateIds()) { + updates.put(id, getUpdateRequest(id, 5000, null)); + } + } + + @AfterClass + public static void stop() throws IOException { + + } + + @Before + public void beginTest() throws IOException { + client = new SPARQL11Protocol(); + final Response ret = client.update(updates.get("DELETE_ALL")); + assertFalse(String.valueOf(ret), ret.isError()); } @After - public void tearDown() throws IOException { - // if(client != null) { - // client.close(); - // } + public void endTest() throws IOException { + if (client != null) { + client.close(); + } } - @Test(timeout=5000) - public void UpdateTest() throws IOException{ - SPARQL11Protocol client = new SPARQL11Protocol(); - UpdateRequest req = getUpdateRequest("U1",5000,null); - final Response ret = client.update(req); - client.close(); - assertFalse(String.valueOf(ret),ret.isError()); - } - - @Test(timeout=5000) - public void QueryTest() throws IOException{ - SPARQL11Protocol client = new SPARQL11Protocol(); - QueryRequest req = getQueryRequest("Q1",5000,null); - final Response ret = client.query(req); - client.close(); - assertFalse(String.valueOf(ret),ret.isError()); - } - - private static UpdateRequest getUpdateRequest(String id,int timeout,String authorization) { + @Test(timeout = 5000) + public void Update() throws IOException { + client = new SPARQL11Protocol(); + final Response ret = client.update(updates.get("U1")); + assertFalse(String.valueOf(ret), ret.isError()); + } + + @Test(timeout = 5000) + public void Query() throws IOException { + client = new SPARQL11Protocol(); + final Response ret = client.query(queries.get("Q1")); + assertFalse(String.valueOf(ret), ret.isError()); + } + + @Test(timeout = 5000) + public void UpdateAndQueryOneResult() throws IOException { + client = new SPARQL11Protocol(); + final Response updateRes = client.update(updates.get("U2")); + final Response queryRes = client.query(queries.get("Q2")); + + if (updateRes.isError()) + assertFalse("Update: " + String.valueOf(updateRes), true); + else if (queryRes.isError()) + assertFalse(" Query: " + String.valueOf(queryRes), true); + else + assertFalse(String.valueOf(queryRes), ((QueryResponse) queryRes).getBindingsResults().size() != 1); + } + + @Test(timeout = 5000) + public void SubscribeAndNotifyOneAddedResult() + throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException { + client = new SPARQL11SEProtocol(protocols.get("Q2"), subHandler); + + final Response subRes = ((SPARQL11SEProtocol) client).subscribe(subscribes.get("Q2")); + final Response updateRes = client.update(updates.get("U2")); + + Response notify = subHandler.getResponse(); + + if (subRes.isError()) + assertFalse(" Subscribe: " + String.valueOf(subRes), true); + else if (updateRes.isError()) + assertFalse("Update: " + String.valueOf(updateRes), true); + else if (notify.isError()) + assertFalse(String.valueOf(notify), true); + else if (((Notification)notify).getARBindingsResults().getAddedBindings().size() != 1) assertFalse(String.valueOf(notify), true); + + } + + private static UpdateRequest getUpdateRequest(String id, int timeout, String authorization) { HTTPMethod method = properties.getUpdateMethod(id); String scheme = properties.getUpdateProtocolScheme(id); String host = properties.getUpdateHost(id); @@ -68,11 +128,12 @@ private static UpdateRequest getUpdateRequest(String id,int timeout,String autho String sparql = properties.getSPARQLUpdate(id); String graphUri = properties.getUsingGraphURI(id); String namedGraphUri = properties.getUsingNamedGraphURI(id); - - return new UpdateRequest( method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, authorization); - } - - private static QueryRequest getQueryRequest(String id,int timeout,String authorization) { + + return new UpdateRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, + authorization); + } + + private static QueryRequest getQueryRequest(String id, int timeout, String authorization) { HTTPMethod method = properties.getQueryMethod(id); String scheme = properties.getQueryProtocolScheme(id); String host = properties.getQueryHost(id); @@ -81,73 +142,16 @@ private static QueryRequest getQueryRequest(String id,int timeout,String authori String sparql = properties.getSPARQLQuery(id); String graphUri = properties.getDefaultGraphURI(id); String namedGraphUri = properties.getNamedGraphURI(id); - - return new QueryRequest( method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, authorization); - } - - // @Test(timeout=5000) - // public void Update(){ - // final Response update = SubmitUpdate(client,TestQueries.SIMPLE_UPDATE); - // assertFalse(String.valueOf(update),update.isError()); - // } - // - // @Test(timeout=5000) - // public void Query(){ - // final Response response = SubmitQuery(client, TestQueries.SIMPLE_QUERY); - // assertFalse(String.valueOf(response),response.isError()); - // } - // - // @Test(timeout=5000) - // public void Subscribe(){ - // final Response response = submitSubscribe(TestQueries.SIMPLE_QUERY, client); - // assertFalse(String.valueOf(response),response.isError()); - // } - // - // @Test(timeout=20000) - // public void NotificationTest() throws InterruptedException { - // final Response response = submitSubscribe(TestQueries.NOTIF_QUERY, client); - // assertFalse(String.valueOf(response),response.isError()); - // - // final Response update = SubmitUpdate(client,TestQueries.NOTIF_UPDATE); - // assertFalse(String.valueOf(update),update.isError()); - // - // final Response notification = subHandler.getResponse(); - // assertFalse(String.valueOf(notification),notification.isError()); - // } - // - // @Test - // public void VerifiedUTF8Update(){ - // Update(); - // - // final Response response = SubmitQuery(client, TestQueries.UTF8_RESULT_QUERY); - // assertFalse(String.valueOf(response),response.isError()); - // - // QueryResponse queryResponse = (QueryResponse) response; - // List results = queryResponse.getBindingsResults().getBindings(); - // assertTrue("Query results empty",results.size() > 0 ); - // - // Bindings bindings = results.get(0); - // assertTrue("Binding variable is not a literal",bindings.isLiteral("o")); - // - // String value = bindings.getValue("o"); - // assertEquals("Incorrect utf-8 value","測試",value); - // } - // - // private static Response SubmitQuery(SPARQL11SEProtocol client,String query) { - // final QueryRequest queryRequest = new QueryRequest(query); - // - // return client.query(queryRequest); - // } - // - // private static Response SubmitUpdate(SPARQL11SEProtocol client,String query) - // { - // final UpdateRequest updateRequest = new UpdateRequest(query); - // return client.update(updateRequest); - // } - // - // private static Response submitSubscribe(String query, SPARQL11SEProtocol - // client) { - // SubscribeRequest sub = new SubscribeRequest(query,null,null,null,null); - // return client.subscribe(sub); - // } + + return new QueryRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, + authorization); + } + + private static SubscribeRequest getSubscribeRequest(String id, String authorization) { + String sparql = properties.getSPARQLQuery(id); + String graphUri = properties.getDefaultGraphURI(id); + String namedGraphUri = properties.getNamedGraphURI(id); + + return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization); + } } \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java index d857cd36..f9420bfa 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java @@ -9,37 +9,27 @@ public class MockSubscriptionHandler implements ISubscriptionHandler { private Response response; - private boolean ping = false; - private Semaphore pingSemaphore = new Semaphore(0); - private Semaphore notSemaphore = new Semaphore(0); + private Semaphore mutex = new Semaphore(0); @Override public void onSemanticEvent(Notification notify) { - this.response = notify; - notSemaphore.release(); + response = notify; + mutex.release(); } - @Override public void onBrokenConnection() { - pingSemaphore.release(); - notSemaphore.release(); + mutex.release(); } @Override public void onError(ErrorResponse errorResponse) { - pingSemaphore.release(); response = errorResponse; - notSemaphore.release(); - } - - public boolean pingRecived() throws InterruptedException { - pingSemaphore.acquire(); - return ping; + mutex.release(); } public Response getResponse() throws InterruptedException { - notSemaphore.acquire(); + mutex.acquire(); return response; } } diff --git a/client-api/src/test/resources/sepatest.jsap b/client-api/src/test/resources/sepatest.jsap index 011cd293..1733ac45 100644 --- a/client-api/src/test/resources/sepatest.jsap +++ b/client-api/src/test/resources/sepatest.jsap @@ -95,6 +95,9 @@ ] }, "updates": { + "DELETE_ALL" : { + "sparql" : "delete where {?x ?y ?z}" + }, "U1": { "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" }, From 77968caeed08c90eb3ed017970da972032bf169a Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 4 Jul 2018 10:20:44 +0200 Subject: [PATCH 22/76] Implemented authentication on endpoint --- .../commons/protocol/SPARQL11Properties.java | 19 +- .../security/AuthenticationProperties.java | 9 + client-api/src/test/resources/dev.jsap | 46 --- engine/endpoints/endpoint-blazegraph.jpar | 10 + engine/endpoints/endpoint-fuseki.jpar | 46 ++- engine/endpoints/endpoint-stardog.jpar | 16 +- engine/endpoints/endpoint-virtuoso.jpar | 12 +- engine/endpoints/endpoint.jpar | 23 -- engine/logs/20180411_12_53_47.csv | 372 ------------------ engine/logs/20180412_13_45_37.csv | 85 ---- engine/logs/20180412_13_55_07.csv | 27 -- engine/logs/20180412_13_59_52.csv | 0 engine/logs/20180412_14_00_13.csv | 0 engine/logs/20180412_14_01_11.csv | 0 engine/logs/20180412_14_01_21.csv | 0 engine/logs/20180412_14_03_44.csv | 0 engine/logs/20180419_09_42_39.csv | 0 .../engine/processing/QueryProcessor.java | 26 +- .../engine/processing/UpdateProcessor.java | 15 +- engine/src/main/resources/log4j2.xml | 8 +- .../.gitignore | 1 - tools/sepatest-secure.jsap | 129 +++++- 22 files changed, 243 insertions(+), 601 deletions(-) delete mode 100644 client-api/src/test/resources/dev.jsap delete mode 100644 engine/endpoints/endpoint.jpar delete mode 100644 engine/logs/20180411_12_53_47.csv delete mode 100644 engine/logs/20180412_13_45_37.csv delete mode 100644 engine/logs/20180412_13_55_07.csv delete mode 100644 engine/logs/20180412_13_59_52.csv delete mode 100644 engine/logs/20180412_14_00_13.csv delete mode 100644 engine/logs/20180412_14_01_11.csv delete mode 100644 engine/logs/20180412_14_01_21.csv delete mode 100644 engine/logs/20180412_14_03_44.csv delete mode 100644 engine/logs/20180419_09_42_39.csv delete mode 100644 tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java index d193ac72..f65e5c88 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Properties.java @@ -147,10 +147,6 @@ public SPARQL11Properties(String propertiesFile) throws SEPAPropertiesException public SPARQL11Properties(String propertiesFile, byte[] secret) throws SEPAPropertiesException { this(propertiesFile); } -// -// public SPARQL11Properties(File jsapFile, byte[] secret) throws SEPAPropertiesException { -// loadProperties(jsapFile); -// } private void loadProperties(String jsapFile) throws SEPAPropertiesException { try (final FileReader in = new FileReader(jsapFile)) { @@ -171,10 +167,16 @@ private void loadProperties(String jsapFile) throws SEPAPropertiesException { throw new SEPAPropertiesException(e1); } - logger.warn("USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed) and run again the broker"); + logger.warn("USING DEFAULTS. Edit \"" + defaultsFileName + "\" (if needed)"); + + this.propertiesFile = defaultsFileName; } } + public String getFilename() { + return propertiesFile; + } + public String toString() { return jsap.get("sparql11protocol").toString(); } @@ -219,13 +221,6 @@ protected void defaults() { update.add("format", new JsonPrimitive("JSON")); sparql11protocol.add("update", update); - // JsonObject graphs = new JsonObject(); - // graphs.add("default-graph-uri", new JsonPrimitive("http://default")); - // graphs.add("named-graph-uri", new JsonPrimitive("http://default")); - // graphs.add("using-graph-uri", new JsonPrimitive("http://default")); - // graphs.add("using-named-graph-uri", new JsonPrimitive("http://default")); - // jsap.add("graphs", graphs); - jsap.add("sparql11protocol", sparql11protocol); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java index d71f4646..802372a4 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java @@ -86,6 +86,15 @@ public AuthenticationProperties(String jsapFileName) throws SEPAPropertiesExcept this(jsapFileName, null); } + public boolean isEnabled() { + try { + return jsap.get("oauth").getAsJsonObject().get("enable").getAsBoolean(); + } + catch(Exception e) { + return false; + } + } + private void validate() throws SEPAPropertiesException { try { jsap.get("oauth").getAsJsonObject().get("enable").getAsBoolean(); diff --git a/client-api/src/test/resources/dev.jsap b/client-api/src/test/resources/dev.jsap deleted file mode 100644 index 1266f08f..00000000 --- a/client-api/src/test/resources/dev.jsap +++ /dev/null @@ -1,46 +0,0 @@ -{ - "host": "localhost", - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/subscribe" - } - }, - "security": { - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure", - "client_id": "ryd7aRCosLlFYowQ5jid0lOlEdMhmbBFFCRZy6fA8KZtsQnuqhfg1zXbzCxpNPih", - "client_secret": "N5UgtT0EWMYV3aIsC+iyAB5kk2maR8MrEXV1+NaEocLFz4eYeVT1mKoRIDwgbpsJ", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfsRgSntEVGcQQR//AJ7psDRK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmjDdzXXWr/jf1HAP4lTU6IeQqN57UDzW9Mr4Y/+5CnidJI5/KxklwtkuhlzeMQupyx34x2j1BAIJLuPIZHnU1oufaKo3I4b6us1FC0JxtftwpFmv5bnXCHKhsygqwQiqSoS6//kP9HugQjTW7b7kFcUsSriJhJNxztijHcYC1YSpLts3699qLcVmlh0+rF5lDncLv15C4CrA4Ro5BGp6AoAChOSB9WFxmivcFefl9V01XlsXMV64aVHzjFMWWXeLR0TXTDIih8LRJKFcAmM4M6ULqqyl9ELprG9axFSN2GINfkTopEKcGeCWEwJRbHxzor7qgxAzafboWDRb1J0DQxvTNuzK47b9i8QGRiWVkiS6rGnAArdIIiLLPzqOfR0/aGwuFrHfGoxxrJJvatKH9+IwZKW/lSg5XU23MSqKrLALOCInXDVMiK25w0nAff4P0hAnCEkUzkntRbGTrcPuUV8j298e7/NmelTlHqw8xZMf+0iZMBQrgzuXWni4Ok9+bMu65HZ4pBHEBJz5296s093a9/GHcRZaL6jUwd31/MD08PdBu6SYUVS7YKqND6U1jKt9w25i4xnOoJ9sBPcIMDCmgia/gqjR8kaTSgABEogibvwQvDnLpgcHKkh7U5lRegzxeDVO1V63Xj1tJgeGbB", - "expires": "QWAE7shP6t6lAuzmw5A8ZQ==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - } - }, - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } -} \ No newline at end of file diff --git a/engine/endpoints/endpoint-blazegraph.jpar b/engine/endpoints/endpoint-blazegraph.jpar index e76e998c..456ec20a 100644 --- a/engine/endpoints/endpoint-blazegraph.jpar +++ b/engine/endpoints/endpoint-blazegraph.jpar @@ -1,5 +1,15 @@ { "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, "sparql11protocol": { "protocol": "http", "port": 9999, diff --git a/engine/endpoints/endpoint-fuseki.jpar b/engine/endpoints/endpoint-fuseki.jpar index 008afb3d..60778685 100644 --- a/engine/endpoints/endpoint-fuseki.jpar +++ b/engine/endpoints/endpoint-fuseki.jpar @@ -1,19 +1,33 @@ { - "parameters" : { - "host" : "localhost" , - "ports" : { - "http" : 3030} - , - "paths" : { - "query" : "/ds/query" , - "update" : "/ds/update"} - , - "methods" : { - "query" : "POST" , - "update" : "URL_ENCODED_POST"} - , - "formats" : { - "query" : "JSON" , - "update" : "HTML"} + "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, + "graphs": { + "default-graph-uri": "http://default", + "named-graph-uri": "http://default", + "using-graph-uri": "http://default", + "using-named-graph-uri": "http://default" + }, + "sparql11protocol": { + "protocol": "http", + "port": 3030, + "query": { + "path": "/ds/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/ds/update", + "method": "URL_ENCODED_POST", + "format": "HTML" } + } } diff --git a/engine/endpoints/endpoint-stardog.jpar b/engine/endpoints/endpoint-stardog.jpar index 651bc5bc..5e67f3ac 100644 --- a/engine/endpoints/endpoint-stardog.jpar +++ b/engine/endpoints/endpoint-stardog.jpar @@ -1,5 +1,15 @@ { "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, "sparql11protocol": { "protocol": "http", "port": 5280, @@ -12,12 +22,6 @@ "path": "/dotone/update", "method": "POST", "format": "JSON" - }, - "authentication": { - "basic": { - "user": "admin", - "pass": "admin" - } } }, "graphs": { diff --git a/engine/endpoints/endpoint-virtuoso.jpar b/engine/endpoints/endpoint-virtuoso.jpar index 637d6456..3a54bbcc 100644 --- a/engine/endpoints/endpoint-virtuoso.jpar +++ b/engine/endpoints/endpoint-virtuoso.jpar @@ -1,5 +1,15 @@ { - "host": "mml.arces.unibo.it", + "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, "graphs": { "default-graph-uri": "http://default", "named-graph-uri": "http://default", diff --git a/engine/endpoints/endpoint.jpar b/engine/endpoints/endpoint.jpar deleted file mode 100644 index 61d25839..00000000 --- a/engine/endpoints/endpoint.jpar +++ /dev/null @@ -1,23 +0,0 @@ -{ - "host": "mml.arces.unibo.it", - "sparql11protocol": { - "protocol": "http", - "port": 8890, - "query": { - "path": "/sparql", - "method": "GET", - "format": "JSON" - }, - "update": { - "path": "/sparql", - "method": "GET", - "format": "JSON" - } - }, - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } -} diff --git a/engine/logs/20180411_12_53_47.csv b/engine/logs/20180411_12_53_47.csv deleted file mode 100644 index 96109542..00000000 --- a/engine/logs/20180411_12_53_47.csv +++ /dev/null @@ -1,372 +0,0 @@ - -UPDATE -1 REQUEST 1523455100078 -UPDATE -1 SCHEDULING 1523455100079 -UPDATE 36 SCHEDULED 1523455100079 -UPDATE 36 ENDPOINT_REQUEST 1523455100079 -UPDATE 36 ENDPOINT_RESPONSE 1523455100182 -UPDATE 36 RESPONDING 1523455100182 -UPDATE 36 RESPONSE_SENT 1523455100183 -UPDATE -1 REQUEST 1523455100199 -UPDATE -1 SCHEDULING 1523455100200 -UPDATE 37 SCHEDULED 1523455100200 -UPDATE 37 ENDPOINT_REQUEST 1523455100200 -UPDATE 37 ENDPOINT_RESPONSE 1523455100290 -UPDATE 37 RESPONDING 1523455100291 -UPDATE 37 RESPONSE_SENT 1523455100291 -UPDATE -1 REQUEST 1523455100293 -UPDATE -1 SCHEDULING 1523455100293 -UPDATE 38 SCHEDULED 1523455100293 -UPDATE 38 ENDPOINT_REQUEST 1523455100293 -UPDATE 38 ENDPOINT_RESPONSE 1523455100387 -UPDATE 38 RESPONDING 1523455100388 -UPDATE 38 RESPONSE_SENT 1523455100388 -UPDATE -1 REQUEST 1523455100390 -UPDATE -1 SCHEDULING 1523455100390 -UPDATE 39 SCHEDULED 1523455100390 -UPDATE 39 ENDPOINT_REQUEST 1523455100390 -UPDATE 39 ENDPOINT_RESPONSE 1523455100783 -UPDATE 39 RESPONDING 1523455100783 -UPDATE 39 RESPONSE_SENT 1523455100783 -UPDATE -1 REQUEST 1523455100785 -UPDATE -1 SCHEDULING 1523455100785 -UPDATE 40 SCHEDULED 1523455100785 -UPDATE 40 ENDPOINT_REQUEST 1523455100785 -UPDATE 40 ENDPOINT_RESPONSE 1523455100911 -UPDATE 40 RESPONDING 1523455100912 -UPDATE 40 RESPONSE_SENT 1523455100912 -UPDATE -1 REQUEST 1523455100913 -UPDATE -1 SCHEDULING 1523455100914 -UPDATE 41 SCHEDULED 1523455100914 -UPDATE 41 ENDPOINT_REQUEST 1523455100914 -UPDATE 41 ENDPOINT_RESPONSE 1523455101351 -UPDATE 41 RESPONDING 1523455101352 -UPDATE 41 RESPONSE_SENT 1523455101352 -UPDATE -1 REQUEST 1523455101355 -UPDATE -1 SCHEDULING 1523455101356 -UPDATE 42 SCHEDULED 1523455101356 -UPDATE 42 ENDPOINT_REQUEST 1523455101356 -UPDATE 42 ENDPOINT_RESPONSE 1523455101468 -UPDATE 42 RESPONDING 1523455101468 -UPDATE 42 RESPONSE_SENT 1523455101468 -UPDATE -1 REQUEST 1523455101471 -UPDATE -1 SCHEDULING 1523455101471 -UPDATE 43 ENDPOINT_REQUEST 1523455101471 -UPDATE 43 SCHEDULED 1523455101471 -UPDATE 43 ENDPOINT_RESPONSE 1523455101527 -UPDATE 43 RESPONDING 1523455101528 -UPDATE 43 RESPONSE_SENT 1523455101528 -UPDATE -1 REQUEST 1523455101530 -UPDATE -1 SCHEDULING 1523455101530 -UPDATE 44 SCHEDULED 1523455101531 -UPDATE 44 ENDPOINT_REQUEST 1523455101531 -UPDATE 44 ENDPOINT_RESPONSE 1523455101618 -UPDATE 44 RESPONDING 1523455101618 -UPDATE 44 RESPONSE_SENT 1523455101618 -UPDATE -1 REQUEST 1523455101620 -UPDATE -1 SCHEDULING 1523455101620 -UPDATE 45 SCHEDULED 1523455101621 -UPDATE 45 ENDPOINT_REQUEST 1523455101621 -UPDATE 45 ENDPOINT_RESPONSE 1523455101680 -UPDATE 45 RESPONDING 1523455101680 -UPDATE 45 RESPONSE_SENT 1523455101681 -UPDATE -1 REQUEST 1523455101686 -UPDATE -1 SCHEDULING 1523455101687 -UPDATE 46 SCHEDULED 1523455101687 -UPDATE 46 ENDPOINT_REQUEST 1523455101687 -UPDATE 46 ENDPOINT_RESPONSE 1523455101788 -UPDATE 46 RESPONDING 1523455101788 -UPDATE 46 RESPONSE_SENT 1523455101789 -QUERY -1 REQUEST 1523455101791 -QUERY -1 SCHEDULING 1523455101791 -QUERY 47 SCHEDULED 1523455101791 -QUERY 47 ENDPOINT_REQUEST 1523455101792 -QUERY 47 ENDPOINT_RESPONSE 1523455101859 -QUERY 47 RESPONDING 1523455101859 -QUERY 47 RESPONSE_SENT 1523455101859 -UPDATE -1 REQUEST 1523455101865 -UPDATE -1 SCHEDULING 1523455101866 -UPDATE 48 SCHEDULED 1523455101866 -UPDATE 48 ENDPOINT_REQUEST 1523455101866 -UPDATE 48 ENDPOINT_RESPONSE 1523455101926 -UPDATE 48 RESPONDING 1523455101927 -UPDATE 48 RESPONSE_SENT 1523455101927 -UPDATE -1 REQUEST 1523455101929 -UPDATE -1 SCHEDULING 1523455101929 -UPDATE 49 SCHEDULED 1523455101929 -UPDATE 49 ENDPOINT_REQUEST 1523455101929 -UPDATE 49 ENDPOINT_RESPONSE 1523455102027 -UPDATE 49 RESPONDING 1523455102027 -UPDATE 49 RESPONSE_SENT 1523455102027 -UPDATE -1 REQUEST 1523455102029 -UPDATE -1 SCHEDULING 1523455102029 -UPDATE 50 SCHEDULED 1523455102030 -UPDATE 50 ENDPOINT_REQUEST 1523455102030 -UPDATE 50 ENDPOINT_RESPONSE 1523455102085 -UPDATE 50 RESPONDING 1523455102085 -UPDATE 50 RESPONSE_SENT 1523455102086 -UPDATE -1 REQUEST 1523455102089 -UPDATE -1 SCHEDULING 1523455102089 -UPDATE 51 ENDPOINT_REQUEST 1523455102090 -UPDATE 51 SCHEDULED 1523455102089 -UPDATE 51 ENDPOINT_RESPONSE 1523455102173 -UPDATE 51 RESPONDING 1523455102173 -UPDATE 51 RESPONSE_SENT 1523455102173 -UPDATE -1 REQUEST 1523455102175 -UPDATE -1 SCHEDULING 1523455102175 -UPDATE 52 SCHEDULED 1523455102175 -UPDATE 52 ENDPOINT_REQUEST 1523455102175 -UPDATE 52 ENDPOINT_RESPONSE 1523455102316 -UPDATE 52 RESPONDING 1523455102316 -UPDATE 52 RESPONSE_SENT 1523455102317 -UPDATE -1 REQUEST 1523455102319 -UPDATE -1 SCHEDULING 1523455102319 -UPDATE 53 SCHEDULED 1523455102320 -UPDATE 53 ENDPOINT_REQUEST 1523455102320 -UPDATE 53 ENDPOINT_RESPONSE 1523455102458 -UPDATE 53 RESPONDING 1523455102459 -UPDATE 53 RESPONSE_SENT 1523455102459 -UPDATE -1 REQUEST 1523455102462 -UPDATE -1 SCHEDULING 1523455102463 -UPDATE 54 SCHEDULED 1523455102463 -UPDATE 54 ENDPOINT_REQUEST 1523455102463 -UPDATE 54 ENDPOINT_RESPONSE 1523455102504 -UPDATE 54 RESPONDING 1523455102505 -UPDATE 54 RESPONSE_SENT 1523455102505 -UPDATE -1 REQUEST 1523455102507 -UPDATE -1 SCHEDULING 1523455102508 -UPDATE 55 ENDPOINT_REQUEST 1523455102508 -UPDATE 55 SCHEDULED 1523455102508 -UPDATE 55 ENDPOINT_RESPONSE 1523455102613 -UPDATE 55 RESPONDING 1523455102614 -UPDATE 55 RESPONSE_SENT 1523455102614 -UPDATE -1 REQUEST 1523455102617 -UPDATE -1 SCHEDULING 1523455102618 -UPDATE 56 SCHEDULED 1523455102618 -UPDATE 56 ENDPOINT_REQUEST 1523455102618 -UPDATE 56 ENDPOINT_RESPONSE 1523455102694 -UPDATE 56 RESPONDING 1523455102695 -UPDATE 56 RESPONSE_SENT 1523455102695 -UPDATE -1 REQUEST 1523455102697 -UPDATE -1 SCHEDULING 1523455102698 -UPDATE 57 SCHEDULED 1523455102698 -UPDATE 57 ENDPOINT_REQUEST 1523455102698 -UPDATE 57 ENDPOINT_RESPONSE 1523455102753 -UPDATE 57 RESPONDING 1523455102754 -UPDATE 57 RESPONSE_SENT 1523455102754 -QUERY -1 REQUEST 1523455102757 -QUERY -1 SCHEDULING 1523455102757 -QUERY 58 SCHEDULED 1523455102757 -QUERY 58 ENDPOINT_REQUEST 1523455102758 -QUERY 58 ENDPOINT_RESPONSE 1523455102881 -QUERY 58 RESPONDING 1523455102881 -QUERY 58 RESPONSE_SENT 1523455102882 -QUERY -1 REQUEST 1523455102883 -QUERY -1 SCHEDULING 1523455102884 -QUERY 59 SCHEDULED 1523455102884 -QUERY 59 ENDPOINT_REQUEST 1523455102884 -QUERY 59 ENDPOINT_RESPONSE 1523455103001 -QUERY 59 RESPONDING 1523455103001 -QUERY 59 RESPONSE_SENT 1523455103002 -QUERY -1 REQUEST 1523455103003 -QUERY -1 SCHEDULING 1523455103004 -QUERY 60 SCHEDULED 1523455103004 -QUERY 60 ENDPOINT_REQUEST 1523455103004 -QUERY 60 ENDPOINT_RESPONSE 1523455103079 -QUERY 60 RESPONDING 1523455103079 -QUERY 60 RESPONSE_SENT 1523455103080 -QUERY -1 REQUEST 1523455103081 -QUERY -1 SCHEDULING 1523455103082 -QUERY 61 SCHEDULED 1523455103082 -QUERY 61 ENDPOINT_REQUEST 1523455103082 -QUERY 61 ENDPOINT_RESPONSE 1523455103171 -QUERY 61 RESPONDING 1523455103171 -QUERY 61 RESPONSE_SENT 1523455103171 -QUERY -1 REQUEST 1523455103173 -QUERY -1 SCHEDULING 1523455103173 -QUERY 62 SCHEDULED 1523455103173 -QUERY 62 ENDPOINT_REQUEST 1523455103174 -QUERY 62 ENDPOINT_RESPONSE 1523455103252 -QUERY 62 RESPONDING 1523455103252 -QUERY 62 RESPONSE_SENT 1523455103252 -QUERY -1 REQUEST 1523455103254 -QUERY -1 SCHEDULING 1523455103254 -QUERY 63 SCHEDULED 1523455103254 -QUERY 63 ENDPOINT_REQUEST 1523455103255 -QUERY 63 ENDPOINT_RESPONSE 1523455103315 -QUERY 63 RESPONDING 1523455103315 -QUERY 63 RESPONSE_SENT 1523455103315 -QUERY -1 REQUEST 1523455103317 -QUERY -1 SCHEDULING 1523455103318 -QUERY 64 SCHEDULED 1523455103318 -QUERY 64 ENDPOINT_REQUEST 1523455103318 -QUERY 64 ENDPOINT_RESPONSE 1523455103401 -QUERY 64 RESPONDING 1523455103402 -QUERY 64 RESPONSE_SENT 1523455103403 -QUERY -1 REQUEST 1523455103405 -QUERY -1 SCHEDULING 1523455103406 -QUERY 65 SCHEDULED 1523455103406 -QUERY 65 ENDPOINT_REQUEST 1523455103407 -QUERY 65 ENDPOINT_RESPONSE 1523455103503 -QUERY 65 RESPONDING 1523455103504 -QUERY 65 RESPONSE_SENT 1523455103504 -QUERY -1 REQUEST 1523455103505 -QUERY -1 SCHEDULING 1523455103506 -QUERY 66 SCHEDULED 1523455103506 -QUERY 66 ENDPOINT_REQUEST 1523455103506 -QUERY 66 ENDPOINT_RESPONSE 1523455103585 -QUERY 66 RESPONDING 1523455103586 -QUERY 66 RESPONSE_SENT 1523455103586 -QUERY -1 REQUEST 1523455103588 -QUERY -1 SCHEDULING 1523455103588 -QUERY 67 SCHEDULED 1523455103588 -QUERY 67 ENDPOINT_REQUEST 1523455103588 -QUERY 67 ENDPOINT_RESPONSE 1523455103685 -QUERY 67 RESPONDING 1523455103685 -QUERY 67 RESPONSE_SENT 1523455103685 -UPDATE -1 REQUEST 1523455103687 -UPDATE -1 SCHEDULING 1523455103687 -UPDATE 68 SCHEDULED 1523455103687 -UPDATE 68 ENDPOINT_REQUEST 1523455103687 -UPDATE 68 ENDPOINT_RESPONSE 1523455103766 -UPDATE 68 RESPONDING 1523455103767 -UPDATE 68 RESPONSE_SENT 1523455103767 -UPDATE -1 REQUEST 1523455103770 -UPDATE -1 SCHEDULING 1523455103770 -UPDATE 69 SCHEDULED 1523455103770 -UPDATE 69 ENDPOINT_REQUEST 1523455103770 -UPDATE 69 ENDPOINT_RESPONSE 1523455103819 -UPDATE 69 RESPONDING 1523455103820 -UPDATE 69 RESPONSE_SENT 1523455103820 -UPDATE -1 REQUEST 1523455103823 -UPDATE -1 SCHEDULING 1523455103824 -UPDATE 70 SCHEDULED 1523455103824 -UPDATE 70 ENDPOINT_REQUEST 1523455103824 -UPDATE 70 ENDPOINT_RESPONSE 1523455103901 -UPDATE 70 RESPONDING 1523455103901 -UPDATE 70 RESPONSE_SENT 1523455103902 -UPDATE -1 REQUEST 1523455103904 -UPDATE -1 SCHEDULING 1523455103905 -UPDATE 71 SCHEDULED 1523455103905 -UPDATE 71 ENDPOINT_REQUEST 1523455103905 -UPDATE 71 ENDPOINT_RESPONSE 1523455103946 -UPDATE 71 RESPONDING 1523455103947 -UPDATE 71 RESPONSE_SENT 1523455103947 -UPDATE -1 REQUEST 1523455103949 -UPDATE -1 SCHEDULING 1523455103950 -UPDATE 72 ENDPOINT_REQUEST 1523455103950 -UPDATE 72 SCHEDULED 1523455103950 -UPDATE 72 ENDPOINT_RESPONSE 1523455104016 -UPDATE 72 RESPONDING 1523455104016 -UPDATE 72 RESPONSE_SENT 1523455104016 -UPDATE -1 REQUEST 1523455104018 -UPDATE -1 SCHEDULING 1523455104018 -UPDATE 73 SCHEDULED 1523455104019 -UPDATE 73 ENDPOINT_REQUEST 1523455104019 -UPDATE 73 ENDPOINT_RESPONSE 1523455104063 -UPDATE 73 RESPONDING 1523455104064 -UPDATE 73 RESPONSE_SENT 1523455104064 -UPDATE -1 REQUEST 1523455104067 -UPDATE -1 SCHEDULING 1523455104067 -UPDATE 74 ENDPOINT_REQUEST 1523455104068 -UPDATE 74 SCHEDULED 1523455104068 -UPDATE 74 ENDPOINT_RESPONSE 1523455104122 -UPDATE 74 RESPONDING 1523455104122 -UPDATE 74 RESPONSE_SENT 1523455104122 -UPDATE -1 REQUEST 1523455104125 -UPDATE -1 SCHEDULING 1523455104125 -UPDATE 75 ENDPOINT_REQUEST 1523455104126 -UPDATE 75 SCHEDULED 1523455104126 -UPDATE 75 ENDPOINT_RESPONSE 1523455104200 -UPDATE 75 RESPONDING 1523455104200 -UPDATE 75 RESPONSE_SENT 1523455104200 -UPDATE -1 REQUEST 1523455104202 -UPDATE -1 SCHEDULING 1523455104202 -UPDATE 76 SCHEDULED 1523455104202 -UPDATE 76 ENDPOINT_REQUEST 1523455104202 -UPDATE 76 ENDPOINT_RESPONSE 1523455104253 -UPDATE 76 RESPONDING 1523455104253 -UPDATE 76 RESPONSE_SENT 1523455104254 -UPDATE -1 REQUEST 1523455104255 -UPDATE -1 SCHEDULING 1523455104256 -UPDATE 77 SCHEDULED 1523455104256 -UPDATE 77 ENDPOINT_REQUEST 1523455104256 -UPDATE 77 ENDPOINT_RESPONSE 1523455104340 -UPDATE 77 RESPONDING 1523455104340 -UPDATE 77 RESPONSE_SENT 1523455104340 -QUERY -1 REQUEST 1523455104343 -QUERY -1 SCHEDULING 1523455104343 -QUERY 78 SCHEDULED 1523455104344 -QUERY 78 ENDPOINT_REQUEST 1523455104344 -QUERY 78 ENDPOINT_RESPONSE 1523455104388 -QUERY 78 RESPONDING 1523455104388 -QUERY 78 RESPONSE_SENT 1523455104388 -UPDATE -1 REQUEST 1523455104390 -UPDATE -1 SCHEDULING 1523455104391 -UPDATE 79 SCHEDULED 1523455104391 -UPDATE 79 ENDPOINT_REQUEST 1523455104391 -UPDATE 79 ENDPOINT_RESPONSE 1523455104442 -UPDATE 79 RESPONDING 1523455104442 -UPDATE 79 RESPONSE_SENT 1523455104442 -UPDATE -1 REQUEST 1523455104444 -UPDATE -1 SCHEDULING 1523455104445 -UPDATE 80 SCHEDULED 1523455104445 -UPDATE 80 ENDPOINT_REQUEST 1523455104445 -UPDATE 80 ENDPOINT_RESPONSE 1523455104507 -UPDATE 80 RESPONDING 1523455104508 -UPDATE 80 RESPONSE_SENT 1523455104508 -UPDATE -1 REQUEST 1523455104510 -UPDATE -1 SCHEDULING 1523455104510 -UPDATE 81 SCHEDULED 1523455104510 -UPDATE 81 ENDPOINT_REQUEST 1523455104510 -UPDATE 81 ENDPOINT_RESPONSE 1523455104549 -UPDATE 81 RESPONDING 1523455104549 -UPDATE 81 RESPONSE_SENT 1523455104550 -UPDATE -1 REQUEST 1523455104552 -UPDATE -1 SCHEDULING 1523455104552 -UPDATE 82 SCHEDULED 1523455104552 -UPDATE 82 ENDPOINT_REQUEST 1523455104552 -UPDATE 82 ENDPOINT_RESPONSE 1523455104638 -UPDATE 82 RESPONDING 1523455104638 -UPDATE 82 RESPONSE_SENT 1523455104639 -UPDATE -1 REQUEST 1523455104641 -UPDATE -1 SCHEDULING 1523455104642 -UPDATE 83 SCHEDULED 1523455104642 -UPDATE 83 ENDPOINT_REQUEST 1523455104642 -UPDATE 83 ENDPOINT_RESPONSE 1523455104730 -UPDATE 83 RESPONDING 1523455104731 -UPDATE 83 RESPONSE_SENT 1523455104731 -UPDATE -1 REQUEST 1523455104734 -UPDATE -1 SCHEDULING 1523455104734 -UPDATE 84 SCHEDULED 1523455104734 -UPDATE 84 ENDPOINT_REQUEST 1523455104734 -UPDATE 84 ENDPOINT_RESPONSE 1523455104833 -UPDATE 84 RESPONDING 1523455104834 -UPDATE 84 RESPONSE_SENT 1523455104834 -UPDATE -1 REQUEST 1523455104835 -UPDATE -1 SCHEDULING 1523455104836 -UPDATE 85 SCHEDULED 1523455104836 -UPDATE 85 ENDPOINT_REQUEST 1523455104836 -UPDATE 85 ENDPOINT_RESPONSE 1523455104888 -UPDATE 85 RESPONDING 1523455104889 -UPDATE 85 RESPONSE_SENT 1523455104889 -UPDATE -1 REQUEST 1523455104891 -UPDATE -1 SCHEDULING 1523455104891 -UPDATE 86 SCHEDULED 1523455104892 -UPDATE 86 ENDPOINT_REQUEST 1523455104892 -UPDATE 86 ENDPOINT_RESPONSE 1523455105018 -UPDATE 86 RESPONDING 1523455105018 -UPDATE 86 RESPONSE_SENT 1523455105019 -UPDATE -1 REQUEST 1523455105020 -UPDATE -1 SCHEDULING 1523455105021 -UPDATE 87 SCHEDULED 1523455105021 -UPDATE 87 ENDPOINT_REQUEST 1523455105021 -UPDATE 87 ENDPOINT_RESPONSE 1523455105121 -UPDATE 87 RESPONDING 1523455105122 -UPDATE 87 RESPONSE_SENT 1523455105122 -UPDATE -1 REQUEST 1523455105125 -UPDATE -1 SCHEDULING 1523455105125 -UPDATE 88 SCHEDULED 1523455105125 -UPDATE 88 ENDPOINT_REQUEST 1523455105125 -UPDATE 88 ENDPOINT_RESPONSE 1523455105230 -UPDATE 88 RESPONDING 1523455105231 -UPDATE 88 RESPONSE_SENT 1523455105231 diff --git a/engine/logs/20180412_13_45_37.csv b/engine/logs/20180412_13_45_37.csv deleted file mode 100644 index 50772280..00000000 --- a/engine/logs/20180412_13_45_37.csv +++ /dev/null @@ -1,85 +0,0 @@ -UPDATE -1 REQUEST 1523540765402 -UPDATE -1 SCHEDULING 1523540765410 -UPDATE 0 SCHEDULED 1523540765413 -UPDATE 0 ENDPOINT_REQUEST 1523540765413 -UPDATE 0 ENDPOINT_RESPONSE 1523540765599 -UPDATE 0 RESPONDING 1523540765604 -UPDATE 0 RESPONSE_SENT 1523540765609 -QUERY -1 REQUEST 1523540836549 -QUERY -1 SCHEDULING 1523540836550 -QUERY 1 SCHEDULED 1523540836550 -QUERY 1 ENDPOINT_REQUEST 1523540836550 -QUERY 1 ENDPOINT_RESPONSE 1523540836675 -QUERY 1 RESPONDING 1523540836676 -QUERY 1 RESPONSE_SENT 1523540836677 -QUERY -1 REQUEST 1523540846167 -QUERY -1 SCHEDULING 1523540846167 -QUERY 2 SCHEDULED 1523540846168 -QUERY 2 ENDPOINT_REQUEST 1523540846168 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -QUERY 2 ENDPOINT_RESPONSE 1523540846238 -ERROR 2 RESPONDING 1523540846238 -ERROR 2 RESPONSE_SENT 1523540846240 -QUERY -1 REQUEST 1523540943673 -QUERY -1 SCHEDULING 1523540943674 -QUERY 3 SCHEDULED 1523540943674 -QUERY 3 ENDPOINT_REQUEST 1523540943674 -QUERY 3 ENDPOINT_RESPONSE 1523540943732 -QUERY 3 RESPONDING 1523540943733 -QUERY 3 RESPONSE_SENT 1523540943734 -UPDATE -1 REQUEST 1523540946010 -UPDATE -1 SCHEDULING 1523540946011 -UPDATE 4 SCHEDULED 1523540946011 -UPDATE 4 ENDPOINT_REQUEST 1523540946011 -UPDATE 4 ENDPOINT_RESPONSE 1523540946067 -UPDATE 4 RESPONDING 1523540946068 -UPDATE 4 RESPONSE_SENT 1523540946068 -QUERY -1 REQUEST 1523540948280 -QUERY -1 SCHEDULING 1523540948280 -QUERY 5 SCHEDULED 1523540948281 -QUERY 5 ENDPOINT_REQUEST 1523540948281 -QUERY 5 ENDPOINT_RESPONSE 1523540948407 -QUERY 5 RESPONDING 1523540948408 -QUERY 5 RESPONSE_SENT 1523540948409 -UPDATE -1 REQUEST 1523540959911 -UPDATE -1 SCHEDULING 1523540959912 -UPDATE 6 SCHEDULED 1523540959912 -UPDATE 6 ENDPOINT_REQUEST 1523540959912 -UPDATE 6 ENDPOINT_RESPONSE 1523540959960 -UPDATE 6 RESPONDING 1523540959961 -UPDATE 6 RESPONSE_SENT 1523540959962 -QUERY -1 REQUEST 1523540961784 -QUERY -1 SCHEDULING 1523540961785 -QUERY 7 SCHEDULED 1523540961785 -QUERY 7 ENDPOINT_REQUEST 1523540961786 -QUERY 7 ENDPOINT_RESPONSE 1523540961875 -QUERY 7 RESPONDING 1523540961876 -QUERY 7 RESPONSE_SENT 1523540961877 -UPDATE -1 REQUEST 1523540996995 -UPDATE -1 SCHEDULING 1523540996995 -UPDATE 8 SCHEDULED 1523540996996 -UPDATE 8 ENDPOINT_REQUEST 1523540996996 -UPDATE 8 ENDPOINT_RESPONSE 1523540997053 -UPDATE 8 RESPONDING 1523540997054 -UPDATE 8 RESPONSE_SENT 1523540997055 -SUBSCRIBE -1 SCHEDULING 138000000 -SUBSCRIBE -1 REQUEST 135000000 -SUBSCRIBE 9 SCHEDULED 1523541001139 -SUBSCRIBE 9 ENDPOINT_REQUEST 1523541001148 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -SUBSCRIBE 9 ENDPOINT_RESPONSE 1523541001190 -Not initialized -SUBSCRIBE -1 SCHEDULING 422000000 -SUBSCRIBE -1 REQUEST 422000000 -SUBSCRIBE 10 SCHEDULED 1523541018423 -SUBSCRIBE 10 ENDPOINT_REQUEST 1523541018427 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -SUBSCRIBE 10 ENDPOINT_RESPONSE 1523541018479 -Not initialized -SUBSCRIBE -1 SCHEDULING 47000000 -SUBSCRIBE -1 REQUEST 47000000 -SUBSCRIBE 11 SCHEDULED 1523541083048 -SUBSCRIBE 11 ENDPOINT_REQUEST 1523541083051 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -SUBSCRIBE 11 ENDPOINT_RESPONSE 1523541174528 -Not initialized diff --git a/engine/logs/20180412_13_55_07.csv b/engine/logs/20180412_13_55_07.csv deleted file mode 100644 index 89b546ec..00000000 --- a/engine/logs/20180412_13_55_07.csv +++ /dev/null @@ -1,27 +0,0 @@ -*** Security check *** -Register: 43663312-cb08-473a-b521-2769cb348dc3 -Register: 43663312-cb08-473a-b521-2769cb348dc3 -Authorize identity:43663312-cb08-473a-b521-2769cb348dc3 -ID:SECRET=de7f54c8-b101-4dd1-b2fe-01a9fb198d8d:e3d45114-1cd0-4e43-9b00-50c7b48c7387 -Authorization Basic ZGU3ZjU0YzgtYjEwMS00ZGQxLWIyZmUtMDFhOWZiMTk4ZDhkOmUzZDQ1MTE0LTFjZDAtNGU0My05YjAwLTUwYzdiNDhjNzM4Nw== -Get token -Credentials: de7f54c8-b101-4dd1-b2fe-01a9fb198d8d e3d45114-1cd0-4e43-9b00-50c7b48c7387 -Access token: eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJTRVBBVGVzdCIsImF1ZCI6WyJodHRwczpcL1wvd290LmFyY2VzLnVuaWJvLml0Ojg0NDNcL3NwYXJxbCIsIndzczpcL1wvd290LmFyY2VzLnVuaWJvLml0Ojk0NDNcL3NwYXJxbCJdLCJuYmYiOjE1MjM1NDEzMDcsImlzcyI6Imh0dHBzOlwvXC93b3QuYXJjZXMudW5pYm8uaXQ6ODQ0M1wvb2F1dGhcL3Rva2VuIiwiZXhwIjoxNTIzNTQxMzEzLCJpYXQiOjE1MjM1NDEzMDgsImp0aSI6ImRlN2Y1NGM4LWIxMDEtNGRkMS1iMmZlLTAxYTlmYjE5OGQ4ZDplM2Q0NTExNC0xY2QwLTRlNDMtOWIwMC01MGM3YjQ4YzczODcifQ.j--osVCHej_Nih-CWtbBxcZgLbkKeoYyoJzZo3mkwQSY7sZIJ9Um_9qKRubhuUm4CBYDhKk3IQBXt7nf_nlbmxPHTSdrxAfmaIX47pOHh7wDk-jjA_DFnzBFAStlemj7RI9nZA1IN26S7IiuJ9KVGAQoa7KImDDj5996-K2VVmzOReSOW5vGY2_HJy0YBE-jDWYNqquyQrvTjPCw4jJLFpff6s0aVMKtMtbg12WYntwF8DixpG6FmuYYB7jF_Nu5rcG78Ehw4DyFu4nD5wWvfb-aOsxk2saHSqDGqI-TTEkpXEN7nDyKbxzd5psVovbYSgHO-61FIjbf0gCzEwpPhw -Validate token -PASSED -********************** -@onOpen WebSocket: Resource descriptor: /subscribe -Message from: /127.0.0.1:50948 [{"subscribe":{"sparql":"PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName }"}}] -SUBSCRIBE -1 SCHEDULING 989000000 -SUBSCRIBE -1 REQUEST 987000000 -Get token #0 (Available: 99) -Subscribe request #0 -SUBSCRIBE 0 SCHEDULED 1523541316991 -SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SPU: sepa://spuid/ab9d1583-1505-4d0b-b898-59e9eb133d49 request: SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SPU init -Process SPARQL query SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SUBSCRIBE 0 ENDPOINT_REQUEST 1523541317001 -Execute SPARQL 1.1 QUERY (timeout: 5000 ms) GET http://mml.arces.unibo.it:8890/sparql?query=PREFIX+td%3A%3Chttp%3A%2F%2Fwot.arces.unibo.it%2Fontology%2Fweb_of_things%23%3E+PREFIX+rdf%3A%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E+PREFIX+qmul%3A%3Chttp%3A%2F%2Feecs.qmul.ac.uk%2Fwot%23%3E+PREFIX+wot%3A%3Chttp%3A%2F%2Fwot.arces.unibo.it%2Fsepa%23%3E+SELECT+%3Fthing+%3Fproperty+%3Faction+%3Fevent+%3FtName+%3FpName+%3FaName+%3FeName+WHERE+%7B%3Fthing+rdf%3Atype+td%3AThing+.+%3Fthing+td%3AhasName+%3FtName+.+OPTIONAL+%7B%3Fthing+td%3AhasEvent+%3Fevent+.+%3Fevent+td%3AhasName+%3FeName+%7D+.+OPTIONAL+%7B+%3Fthing+td%3AhasAction+%3Faction+.+%3Faction+td%3AhasName+%3FaName+%7D+.+OPTIONAL+%7B+%3Fthing+td%3AhasProperty+%3Fproperty+.+%3Fproperty+td%3AhasName+%3FpName+%7D&format=application%2Fsparql-results%2Bjson&default-graph-uri=http%3A%2F%2Fdefault HTTP/1.1 -QUERY_TIME (112 ms) diff --git a/engine/logs/20180412_13_59_52.csv b/engine/logs/20180412_13_59_52.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_00_13.csv b/engine/logs/20180412_14_00_13.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_01_11.csv b/engine/logs/20180412_14_01_11.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_01_21.csv b/engine/logs/20180412_14_01_21.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_03_44.csv b/engine/logs/20180412_14_03_44.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180419_09_42_39.csv b/engine/logs/20180419_09_42_39.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index c687a296..6460d60b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -23,13 +23,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; - +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; import it.unibo.arces.wot.sepa.timing.Timings; @@ -56,17 +58,29 @@ public synchronized Response process(QueryRequest req) { return new ErrorResponse(500, e.getMessage()); } + // Authorized access to the endpoint + String authorizationHeader = null; + try { + // TODO: to implement also bearer authentication + AuthenticationProperties oauth = new AuthenticationProperties(properties.getFilename()); + if (oauth.isEnabled()) + authorizationHeader = oauth.getBasicAuthorizationHeader(); + } catch (SEPAPropertiesException | SEPASecurityException e) { + logger.warn(e.getMessage()); + } + Response ret; QueryRequest request; request = new QueryRequest(req.getToken(), properties.getQueryMethod(), properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), properties.getDefaultQueryPath(), - req.getSPARQL(), req.getTimeout(), req.getDefaultGraphUri(), req.getNamedGraphUri(),req.getAuthorizationHeader()); - + req.getSPARQL(), req.getTimeout(), req.getDefaultGraphUri(), req.getNamedGraphUri(), + authorizationHeader); + ret = endpoint.query(request); - + if (endpointSemaphore != null) - endpointSemaphore.release(); - + endpointSemaphore.release(); + long stop = Timings.getTime(); logger.trace("Response: " + ret.toString()); Timings.log("QUERY_PROCESSING_TIME", start, stop); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index 81f46fc8..d963772d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -23,12 +23,15 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; import it.unibo.arces.wot.sepa.timing.Timings; @@ -55,12 +58,22 @@ public synchronized Response process(UpdateRequest req) { return new ErrorResponse(500, e.getMessage()); } + // Authorized access to the endpoint + String authorizationHeader = null; + try { + //TODO: to implement also bearer authentication + AuthenticationProperties oauth = new AuthenticationProperties(properties.getFilename()); + if (oauth.isEnabled()) authorizationHeader = oauth.getBasicAuthorizationHeader(); + } catch (SEPAPropertiesException | SEPASecurityException e) { + logger.warn(e.getMessage()); + } + // UPDATE the endpoint Response ret; UpdateRequest request = new UpdateRequest(req.getToken(), properties.getUpdateMethod(), properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), properties.getUpdatePath(), req.getSPARQL(), req.getTimeout(), req.getUsingGraphUri(), - req.getUsingNamedGraphUri(), req.getAuthorizationHeader()); + req.getUsingNamedGraphUri(), authorizationHeader); logger.trace(request); ret = endpoint.update(request); diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index e308cae7..626b507e 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -4,18 +4,18 @@ - + diff --git a/tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore b/tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore deleted file mode 100644 index 38ec9779..00000000 --- a/tools/paho1530274988215000000-tcpgiovearcesuniboit52877/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.lck diff --git a/tools/sepatest-secure.jsap b/tools/sepatest-secure.jsap index df620a28..10a3dfe3 100644 --- a/tools/sepatest-secure.jsap +++ b/tools/sepatest-secure.jsap @@ -1 +1,128 @@ -{"host":"localhost","oauth":{"enable":true,"register":"https://localhost:8443/oauth/register","tokenRequest":"https://localhost:8443/oauth/token","client_id":"mzSHL2SAGTXRp51RyEk0hRFSv6AR5oKigsKC3DwnIynX0WukUtEqLzWXc3Sl5/Xm","client_secret":"iGmr/pjQZwDBrQVpr85vRZvctdkHMI+2O3Y6+WaCQ2nZHIFWxXDnO7zi+ERzlDHi","jwt":"xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvZ8mpdZeckpOfsYdzUvDccK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmf04Bp3dhMHflPzxQGiDGsAuxUVGk6vpjI2Ul0ZpjebHGKBkk1w7+7kq8PPWzx5JJ+/M2BYm0FB1wJSDxOc2Jl49RWWhiACu+NmsWR4F8GotoIj4Ny+hZvM0UsSGt4iixXMF72oqV14eJdnf/VnW83vuEnWxCMKLgR6jMa3QW+/p8DJ7dchxaQ2bqhMAlkzrF3ysqfi875lcdLEfxTcyY0nuve7GDICNVT3yDRTZJdkSEuKYx8bAG7VpjAWo2fiIulDo/5i5YAK/uWKbQ8eYeVQYqVwYeYVUeegm4spqq6cLHwZYTIHo4n9P9rpN9u844Nq4KdMsQ2xGO4t0UgWGx5ciqRY5gS3+hF0DW3N2gaN5kfb+lt2XSkcAn1lb6uRFXYBszul5bT8hyDE+NKolX7X16+pOu8CZsdH6t9CXhJSCDSjliTw0Zl0430tX1qzbKakrfcvU99mw6T6T2cSWbZUgocVi80noBggXVgZxwDtyWGFc5eMoKGgx6TavrgAmIQwgP6ZkqULvnYYu5PW8iDuExfaeur/uf3M/L+l+ecwyEkprfYjGBY1LvGNnNZ9ANQel6HyIrssndjLF37Z9dIg/gDTgMujK8meJw9xigTyrqE/MUCal6SK2YgjDtPuOFu3wP2h1k1lcrHd7biUSU2","expires":"AZj3wOp7cD2iFgMUVGVfmA==","type":"XPrHEX2xHy+5IuXHPHigMw=="},"sparql11protocol":{"protocol":"https","port":8443,"query":{"path":"/secure/query","method":"POST","format":"JSON"},"update":{"path":"/secure/update","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"wss","availableProtocols":{"ws":{"port":9000,"path":"/subscribe"},"wss":{"port":9443,"path":"/secure/subscribe"}}},"graphs":{"default-graph-uri":"http://sepatest","named-graph-uri":"http://sepatest","using-graph-uri":"http://sepatest","using-named-graph-uri":"http://sepatest"},"namespaces":{"sepa":"http://wot.arces.unibo.it/sepa#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},"extended":{"notificationMaxDelay":2000,"sequence":[{"id":"RegisterMePlease!","name":"Register not allowed","type":"REGISTER","passed":false},{"id":"SEPATest","name":"Register allowed","type":"REGISTER","passed":true},{"name":"Update (secure)","type":"UPDATE","id":"U1","passed":true},{"name":"Query (secure)","type":"QUERY","id":"Q1","results":1,"passed":true},{"name":"Subscribe (secure)","type":"SUBSCRIBE","id":"Q1","results":1,"passed":true},{"name":"Update (secure)","type":"UPDATE","id":"U2","secure":true,"passed":true},{"name":"Notification","type":"WAIT_NOTIFICATION","passed":true},{"name":"Unsubscribe (secure)","type":"UNSUBSCRIBE","passed":true},{"name":"Update (secure)","type":"UPDATE","id":"U2","passed":true},{"name":"Notification (not received)","type":"WAIT_NOTIFICATION","passed":false}]},"updates":{"U1":{"sparql":"delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}"},"U2":{"sparql":"delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}"}},"queries":{"Q1":{"sparql":"select * where {?x ?y \"ვაიმეე\"}"}}} \ No newline at end of file +{ + "host": "localhost", + "oauth": { + "enable": true, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "mzSHL2SAGTXRp51RyEk0hRFSv6AR5oKigsKC3DwnIynX0WukUtEqLzWXc3Sl5/Xm", + "client_secret": "iGmr/pjQZwDBrQVpr85vRZvctdkHMI+2O3Y6+WaCQ2nZHIFWxXDnO7zi+ERzlDHi", + "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfvZ8mpdZeckpOfsYdzUvDccK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmf04Bp3dhMHflPzxQGiDGsAuxUVGk6vpjI2Ul0ZpjebHGKBkk1w7+7kq8PPWzx5JJ+/M2BYm0FB1wJSDxOc2Jl49RWWhiACu+NmsWR4F8GotoIj4Ny+hZvM0UsSGt4iixXMF72oqV14eJdnf/VnW83vuEnWxCMKLgR6jMa3QW+/p8DJ7dchxaQ2bqhMAlkzrF3ysqfi875lcdLEfxTcyY0nuve7GDICNVT3yDRTZJdkSEuKYx8bAG7VpjAWo2fiIulDo/5i5YAK/uWKbQ8eYeVQYqVwYeYVUeegm4spqq6cLHwZYTIHo4n9P9rpN9u844Nq4KdMsQ2xGO4t0UgWGx5ciqRY5gS3+hF0DW3N2gaN5kfb+lt2XSkcAn1lb6uRFXYBszul5bT8hyDE+NKolX7X16+pOu8CZsdH6t9CXhJSCDSjliTw0Zl0430tX1qzbKakrfcvU99mw6T6T2cSWbZUgocVi80noBggXVgZxwDtyWGFc5eMoKGgx6TavrgAmIQwgP6ZkqULvnYYu5PW8iDuExfaeur/uf3M/L+l+ecwyEkprfYjGBY1LvGNnNZ9ANQel6HyIrssndjLF37Z9dIg/gDTgMujK8meJw9xigTyrqE/MUCal6SK2YgjDtPuOFu3wP2h1k1lcrHd7biUSU2", + "expires": "AZj3wOp7cD2iFgMUVGVfmA==", + "type": "XPrHEX2xHy+5IuXHPHigMw==" + }, + "sparql11protocol": { + "protocol": "https", + "port": 8443, + "query": { + "path": "/secure/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/secure/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "wss", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://sepatest", + "named-graph-uri": "http://sepatest", + "using-graph-uri": "http://sepatest", + "using-named-graph-uri": "http://sepatest" + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "extended": { + "notificationMaxDelay": 2000, + "sequence": [ + { + "id": "RegisterMePlease!", + "name": "Register not allowed", + "type": "REGISTER", + "passed": false + }, + { + "id": "SEPATest", + "name": "Register allowed", + "type": "REGISTER", + "passed": true + }, + { + "name": "Update (secure)", + "type": "UPDATE", + "id": "U1", + "passed": true + }, + { + "name": "Query (secure)", + "type": "QUERY", + "id": "Q1", + "results": 1, + "passed": true + }, + { + "name": "Subscribe (secure)", + "type": "SUBSCRIBE", + "id": "Q1", + "results": 1, + "passed": true + }, + { + "name": "Update (secure)", + "type": "UPDATE", + "id": "U2", + "secure": true, + "passed": true + }, + { + "name": "Notification", + "type": "WAIT_NOTIFICATION", + "passed": true + }, + { + "name": "Unsubscribe (secure)", + "type": "UNSUBSCRIBE", + "passed": true + }, + { + "name": "Update (secure)", + "type": "UPDATE", + "id": "U2", + "passed": true + }, + { + "name": "Notification (not received)", + "type": "WAIT_NOTIFICATION", + "passed": false + } + ] + }, + "updates": { + "U1": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" + }, + "U2": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}" + } + }, + "queries": { + "Q1": { + "sparql": "select * where {?x ?y \"ვაიმეე\"}" + } + } +} \ No newline at end of file From 48d820fe6b91f15d34663515523691d7c9358fd2 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 4 Jul 2018 10:34:58 +0200 Subject: [PATCH 23/76] Added options to the command line to specify engine.jpar and endpoint.jpar file path --- .../arces/wot/sepa/engine/core/Engine.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 4bc46977..b4d3d692 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -59,12 +59,12 @@ * Event Processing Architecture (SEPA) * * @author Luca Roffia (luca.roffia@unibo.it) - * @version 0.9.1 + * @version 0.9.2 */ public class Engine implements EngineMBean { private static Engine engine; - private static String version = "0.9.1"; + private static String version = "0.9.2"; private EngineProperties properties = null; // Scheduler request queue @@ -97,6 +97,8 @@ public class Engine implements EngineMBean { private String jwtAlias = "sepakey"; private String jwtPassword = "sepa2017"; private String serverCertificate = "sepacert"; + private String engineJpar = "engine.jpar"; + private String endpointJpar = "endpoint.jpar"; // Logging file name private void setLoggingFileName() { @@ -113,16 +115,23 @@ private void setLoggingFileName() { private void printUsage() { System.out.println("Usage:"); - System.out.println("java [JMX] [JVM] [LOG4J] -jar SEPAEngine_X.Y.Z.jar [JKS OPTIONS]"); + System.out.println("java [JMX] [JVM] [LOG4J] -jar SEPAEngine_X.Y.Z.jar [-help] [-engine=engine.jpar] [-endpoint=endpoint.jpar] [JKS OPTIONS]"); + System.out.println("Options: "); + System.out.println("-engine : can be used to specify the JSON configuration parameters for the engine (default: engine.jpar)"); + System.out.println("-endpoint : can be used to specify the JSON configuration parameters for the endpoint (default: endpoint.jpar)"); + System.out.println("-help : to print this help"); + System.out.println(""); System.out.println("JMX:"); System.out.println("-Dcom.sun.management.config.file=jmx.properties : to enable JMX remote managment"); + System.out.println(""); System.out.println("JVM:"); System.out.println("-XX:+UseG1GC"); + System.out.println(""); System.out.println("LOG4J"); System.out.println("-Dlog4j.configurationFile=./log4j2.xml"); + System.out.println(""); System.out.println("JKS OPTIONS:"); - System.out.println("-help : to print this help"); System.out.println("-storename= : file name of the JKS (default: sepa.jks)"); System.out.println("-storepwd= : password of the JKS (default: sepa2017)"); System.out.println("-alias= : alias for the JWT key (default: sepakey)"); @@ -155,6 +164,8 @@ private void parsingArgument(String[] args) throws PatternSyntaxException { case "-certificate": serverCertificate = tmp[1]; break; + case "-engine": + default: break; } @@ -199,7 +210,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException // Initialize SPARQL 1.1 SE processing service properties try { - properties = new EngineProperties("engine.jpar"); + properties = new EngineProperties(engineJpar); } catch (SEPAPropertiesException e) { // System.err.println("Failed to load engine.jpar: "+e.getMessage()); // properties = null; @@ -207,7 +218,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException SPARQL11Properties endpointProperties = null; try { - endpointProperties = new SPARQL11Properties("endpoint.jpar"); + endpointProperties = new SPARQL11Properties(endpointJpar ); } catch (SEPAPropertiesException e2) { // System.err.println("Failed to load endpoint.jpar: "+e2.getMessage()); // endpointProperties = null; From 71ca242292c85de03e5461db66001995e589818a Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 4 Jul 2018 10:43:36 +0200 Subject: [PATCH 24/76] New options from command line TESTED --- .../java/it/unibo/arces/wot/sepa/engine/core/Engine.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index b4d3d692..c1524c24 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -165,7 +165,11 @@ private void parsingArgument(String[] args) throws PatternSyntaxException { serverCertificate = tmp[1]; break; case "-engine": - + engineJpar = tmp[1]; + break; + case "-endpoint": + endpointJpar = tmp[1]; + break; default: break; } From ba33c6631eb4d73bfadd6a5ca97d0cf63634249a Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 4 Jul 2018 12:01:01 +0200 Subject: [PATCH 25/76] Implemented parallel execution of updates and queries --- .../sepa/commons/request/QueryRequest.java | 12 +++--- .../wot/sepa/commons/request/Request.java | 35 +++++---------- .../wot/sepa/engine/processing/Processor.java | 43 ++++++++++++++----- .../{ISubscriptionProcUnit.java => ISPU.java} | 2 +- .../engine/processing/subscriptions/SPU.java | 3 +- .../processing/subscriptions/SPUManager.java | 8 ++-- .../processing/subscriptions/SPUSync.java | 6 +-- .../subscriptions/SubscribeProcessor.java | 4 +- .../processing/subscriptions/Subscriber.java | 6 +-- .../subscription/SPUMangerTest.java | 4 +- 10 files changed, 66 insertions(+), 57 deletions(-) rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/{ISubscriptionProcUnit.java => ISPU.java} (87%) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index 530ee342..2005b693 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -23,9 +23,8 @@ import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; -// TODO: Auto-generated Javadoc /** - * This class represents a request to perform a SPARQL 1.1 Query + * This class represents a SPARQL 1.1 Query request * * @see SPARQL 1.1 Query * */ @@ -81,9 +80,12 @@ public QueryRequest(HTTPMethod queryMethod, String queryProtocolScheme, String q this(-1,queryMethod, queryProtocolScheme, queryHost, queryPort,queryPath, string, timeout, defaultGraphURI, namedGraphURI,authorization); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ + @Override + public boolean equals(Object obj) { + if (!obj.getClass().equals(QueryRequest.class)) return false; + return sparql.equals(((QueryRequest)obj).sparql); + } + @Override public String toString() { if (token != -1) return "QUERY #"+token+" "+sparql; return "QUERY "+sparql; diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java index 65f943c3..17e940c8 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java @@ -136,31 +136,16 @@ public boolean isSubscribeRequest() { public boolean isUnsubscribeRequest() { return this.getClass().equals(UnsubscribeRequest.class); } - -// public boolean setBasicAuthentication(String user, String pass) { -// authorization = AUTHENTICATION_SCHEMA.BASIC; -// -// try { -// byte[] buf = Base64.getEncoder().encode((user + ":" + pass).getBytes("UTF-8")); -// basicAuthorizationHeader = "Basic " +new String(buf, "UTF-8"); -// } catch (UnsupportedEncodingException e) { -// return false; -// } -// return true; -// } -// -// public String getAuthorizationHeader() { -// switch (authorization) { -// case BASIC: -// return basicAuthorizationHeader; -// default: -// return null; -// } -// } -// -// public boolean isAuthenticationRequired() { -// return authorization != AUTHENTICATION_SCHEMA.DISABLED; -// } + + /** + * Default implementation. Two requests are equal if they belong to the same class and their SPARQL strings are equals. SPARQL matching should be based on SPARQL algebra + * and SPARQL semantics. The default implementation provides a syntax based matching. + */ + @Override + public boolean equals(Object obj) { + if (!obj.getClass().equals(this.getClass())) return false; + return sparql.equals(((QueryRequest)obj).sparql); + } public HTTPMethod getHttpMethod() { return method; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index 9ef48af4..02367190 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -18,6 +18,7 @@ package it.unibo.arces.wot.sepa.engine.processing; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SubscribeProcessor; @@ -55,6 +56,9 @@ public class Processor extends Thread implements ProcessorMBean { // Concurrent endpoint limit private Semaphore endpointSemaphore = null; + // Update queue + private LinkedBlockingQueue updateQueue = new LinkedBlockingQueue(); + public Processor(SPARQL11Properties endpointProperties, EngineProperties properties, SchedulerRequestResponseQueue queue) throws IllegalArgumentException, SEPAProtocolException { if (queue == null) { @@ -82,6 +86,32 @@ public Processor(SPARQL11Properties endpointProperties, EngineProperties propert ProcessorBeans.setEndpoint(endpointProperties); ProcessorBeans.setQueryTimeout(properties.getQueryTimeout()); ProcessorBeans.setUpdateTimeout(properties.getUpdateTimeout()); + + // Main thread for FIFO scheduling of updates + Thread updateScheduler = new Thread() { + public void run() { + while(true) { + UpdateRequest request; + try { + request = updateQueue.take(); + } catch (InterruptedException e) { + return; + } + // Process update request + request.setTimeout(ProcessorBeans.getUpdateTimeout()); + Response ret = updateProcessor.process(request); + + // // Notify update result + queue.addResponse(ret); + + if (ret.isUpdateResponse()) { + subscribeProcessor.process((UpdateResponse) ret); + } + } + } + }; + updateScheduler.setName("SEPA-Update-Scheduler"); + updateScheduler.start(); } @Override @@ -95,21 +125,14 @@ public void run() { return; } + Request request = scheduledRequest.getRequest(); if (request.isUpdateRequest()) { logger.debug("Update request #" + request.getToken()); logger.trace(request); - // Process update request - request.setTimeout(ProcessorBeans.getUpdateTimeout()); - Response ret = updateProcessor.process((UpdateRequest) request); - - // // Notify update result - queue.addResponse(ret); - - if (ret.isUpdateResponse()) { - subscribeProcessor.process((UpdateResponse) ret); - } + // Put the request into the FIFO queue + updateQueue.offer((UpdateRequest) request); } else if (request.isQueryRequest()) { logger.debug("Query request #" + request.getToken()); logger.trace(request); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java similarity index 87% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java index c628d99a..edd6ec97 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISubscriptionProcUnit.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java @@ -4,7 +4,7 @@ import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -public interface ISubscriptionProcUnit extends Runnable { +public interface ISPU extends Runnable { Response init(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index dc09bd33..28d7d046 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -49,7 +49,7 @@ */ //public abstract class SPU extends Observable implements Runnable { -public abstract class SPU implements ISubscriptionProcUnit { +public abstract class SPU implements ISPU { private final Logger logger; // The URI of the subscription (i.e., sepa://spuid/UUID) @@ -59,7 +59,6 @@ public abstract class SPU implements ISubscriptionProcUnit { // Update queue protected ConcurrentLinkedQueue updateQueue = new ConcurrentLinkedQueue(); - // protected SPARQL11Protocol endpoint = null; protected QueryProcessor queryProcessor; protected SubscribeRequest request; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index 13ea3bb0..41e91c0a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -11,9 +11,9 @@ * on the internal structure. */ public class SPUManager { - private HashMap spus = new HashMap<>(); + private HashMap spus = new HashMap<>(); - public synchronized void register(ISubscriptionProcUnit spu){ + public synchronized void register(ISPU spu){ spus.put(spu.getUUID(),spu); } @@ -29,11 +29,11 @@ public synchronized boolean isValidSpuId(String id){ return spus.containsKey(id); } - public synchronized Iterator filter(UpdateResponse response){ + public synchronized Iterator filter(UpdateResponse response){ return spus.values().iterator(); } - public synchronized Collection getAll(){ + public synchronized Collection getAll(){ return spus.values(); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java index 89f232d2..872d14be 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java @@ -3,7 +3,7 @@ import java.util.Collection; import java.util.HashSet; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISubscriptionProcUnit; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPU; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,9 +14,9 @@ public class SPUSync { private final Logger logger = LogManager.getLogger(); // SPU synchronization - private HashSet processingSpus = new HashSet<>(); + private HashSet processingSpus = new HashSet<>(); - public void startProcessing(Collection spus) { + public void startProcessing(Collection spus) { synchronized (processingSpus) { processingSpus.clear(); processingSpus.addAll(spus); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java index 824a9d5f..6b754ff9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java @@ -51,7 +51,7 @@ public class SubscribeProcessor implements SubscribeProcessorMBean { private SPUManager spuManager = new SPUManager(); // Request queue - private LinkedBlockingQueue subscribeQueue = new LinkedBlockingQueue<>(); + private LinkedBlockingQueue subscribeQueue = new LinkedBlockingQueue<>(); private LinkedBlockingQueue unsubscribeQueue = new LinkedBlockingQueue<>(); private Semaphore endpointSemaphore; @@ -139,7 +139,7 @@ public void process(UpdateResponse update) { // TODO: filter algorithm // Synchronize on spuManger to avoid changes of SPU collection synchronized (spuManager) { - for (ISubscriptionProcUnit spu : spuManager.getAll()) + for (ISPU spu : spuManager.getAll()) spu.process(update); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java index 7d529c25..6b4ae3c7 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -10,10 +10,10 @@ public class Subscriber extends Thread { private final Logger logger = LogManager.getLogger(); private final AtomicBoolean end = new AtomicBoolean(false); - private final BlockingQueue subscriptionQueue; + private final BlockingQueue subscriptionQueue; private final SPUManager spuManager; - public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ + public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ super("SEPA-SPU-Subscriber"); this.subscriptionQueue = subscriptionQueue; spuManager = manager; @@ -22,7 +22,7 @@ public Subscriber(BlockingQueue subscriptionQueue, SPUMan @Override public void run() { while (!end.get()) { - ISubscriptionProcUnit spu = null; + ISPU spu = null; try { spu = subscriptionQueue.take(); // Start the SPU thread diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java index 3be54f2b..c6cccabe 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java @@ -3,7 +3,7 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISubscriptionProcUnit; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; import org.junit.Assert; import org.junit.Before; @@ -46,7 +46,7 @@ public void isValidUIDTest(){ Assert.assertTrue(spuManger.isValidSpuId("Ironman")); } - private class FakeSPU implements ISubscriptionProcUnit{ + private class FakeSPU implements ISPU{ private String uid; From 25dfe8b9263cc3305ce6e77265be74997a331f3d Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 4 Jul 2018 14:06:48 +0200 Subject: [PATCH 26/76] Subscribe processor refactory. Ready to implement "equals queries optimization" --- .../sepa/commons/request/QueryRequest.java | 7 +- .../engine/processing/subscriptions/SPU.java | 4 +- .../processing/subscriptions/SPUManager.java | 110 ++++++++++++++---- .../processing/subscriptions/SPUNaive.java | 2 +- .../subscriptions/SubscribeProcessor.java | 51 ++++---- .../processing/subscriptions/Subscriber.java | 17 ++- .../{Unsubcriber.java => Unsubscriber.java} | 13 ++- tools/chat.jsap | 9 +- 8 files changed, 139 insertions(+), 74 deletions(-) rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/{Unsubcriber.java => Unsubscriber.java} (76%) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index 2005b693..90b721fc 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -79,12 +79,7 @@ public QueryRequest(HTTPMethod queryMethod, String queryProtocolScheme, String q String queryPath, String string, int timeout, String defaultGraphURI, String namedGraphURI,String authorization) { this(-1,queryMethod, queryProtocolScheme, queryHost, queryPort,queryPath, string, timeout, defaultGraphURI, namedGraphURI,authorization); } - - @Override - public boolean equals(Object obj) { - if (!obj.getClass().equals(QueryRequest.class)) return false; - return sparql.equals(((QueryRequest)obj).sparql); - } + @Override public String toString() { if (token != -1) return "QUERY #"+token+" "+sparql; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index 28d7d046..22d9fa46 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -74,10 +74,10 @@ public abstract class SPU implements ISPU { private Response notify; // List of processing SPU - private SPUSync sync; + private SPUManager sync; public SPU(SubscribeRequest subscribe, SPARQL11Properties properties, EventHandler eventHandler, - Semaphore endpointSemaphore, SPUSync sync) throws SEPAProtocolException { + Semaphore endpointSemaphore, SPUManager sync) throws SEPAProtocolException { if (eventHandler == null) throw new SEPAProtocolException(new IllegalArgumentException("Subscribe event handler is null")); if (sync == null) diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index 41e91c0a..f6b3779d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -1,45 +1,105 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** - * SpuManager is a monitor class. It takes care of the SPU collection and it encapsulates filtering algorithms based - * on the internal structure. + * SpuManager is a monitor class. It takes care of the SPU collection and it + * encapsulates filtering algorithms based on the internal structure. */ public class SPUManager { - private HashMap spus = new HashMap<>(); + private final Logger logger = LogManager.getLogger(); + + // Registered SPUs + private HashMap spus = new HashMap<>(); + + // SPUs processing set + private HashSet processingSpus = new HashSet<>(); + + public void startProcessing(UpdateResponse update) { + synchronized (processingSpus) { + processingSpus.clear(); + + Iterator spus = filter(update); + + while (spus.hasNext()) { + ISPU spu = spus.next(); + processingSpus.add(spu); + spu.process(update); + } + + logger.debug("Activated SPUs: " + processingSpus.size()); + } + } + + public void waitEndOfProcessing() { + // Wait all SPUs completing processing (or timeout) + synchronized (processingSpus) { + while (!processingSpus.isEmpty()) { + logger.debug(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); + try { + processingSpus.wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); + } catch (InterruptedException e) { + return; + } + } + // TIMEOUT + if (!processingSpus.isEmpty()) { + logger.error("Timeout on SPU processing. SPUs still running: " + processingSpus.size()); + } + } + } + + public boolean isEmpty() { + return processingSpus.isEmpty(); + } - public synchronized void register(ISPU spu){ - spus.put(spu.getUUID(),spu); - } + public void endProcessing(SPU s) { + synchronized (processingSpus) { + processingSpus.remove(s); + logger.debug("SPUs left: " + processingSpus.size()); + processingSpus.notify(); + } + } - public synchronized void unRegister(String spuID){ - if(!isValidSpuId(spuID)){ - throw new IllegalArgumentException("Unregistering a not existing SPUID: "+ spuID); - } - spus.get(spuID).terminate(); - spus.remove(spuID); - } + public synchronized void register(ISPU spu) { + synchronized (processingSpus) { + spus.put(spu.getUUID(), spu); + } + } - public synchronized boolean isValidSpuId(String id){ - return spus.containsKey(id); - } + public synchronized void unRegister(String spuID) { + synchronized (processingSpus) { + if (!isValidSpuId(spuID)) { + throw new IllegalArgumentException("Unregistering a not existing SPUID: " + spuID); + } + spus.get(spuID).terminate(); + spus.remove(spuID); + } + } - public synchronized Iterator filter(UpdateResponse response){ - return spus.values().iterator(); - } + public synchronized boolean isValidSpuId(String id) { + return spus.containsKey(id); + } - public synchronized Collection getAll(){ - return spus.values(); - } + public synchronized Iterator filter(UpdateResponse response) { + return spus.values().iterator(); + } - public synchronized int size(){ - return spus.values().size(); - } + public synchronized Collection getAll() { + return spus.values(); + } + public synchronized int size() { + return spus.values().size(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index 2a169c6b..83b0040b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -45,7 +45,7 @@ public class SPUNaive extends SPU { private Integer sequence = 1; public SPUNaive(SubscribeRequest subscribe, EventHandler handler, SPARQL11Properties endpointProperties, - Semaphore endpointSemaphore, SPUSync sync) throws SEPAProtocolException { + Semaphore endpointSemaphore, SPUManager sync) throws SEPAProtocolException { super(subscribe, endpointProperties, handler, endpointSemaphore, sync); logger = LogManager.getLogger("SPUNaive" + getUUID()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java index 6b754ff9..4de9dae8 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java @@ -19,7 +19,6 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; import java.time.Instant; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import org.apache.logging.log4j.Logger; @@ -43,22 +42,15 @@ public class SubscribeProcessor implements SubscribeProcessorMBean { private final Logger logger = LogManager.getLogger(); + private final Subscriber subscriber; - private final Unsubcriber unsubscriber; + private final Unsubscriber unsubscriber; private SPARQL11Properties endpointProperties; - - private SPUManager spuManager = new SPUManager(); - - // Request queue - private LinkedBlockingQueue subscribeQueue = new LinkedBlockingQueue<>(); - private LinkedBlockingQueue unsubscribeQueue = new LinkedBlockingQueue<>(); - private Semaphore endpointSemaphore; - - // SPU Synchronization - private SPUSync spuSync = new SPUSync(); - + + private SPUManager spuManager = new SPUManager(); + public SubscribeProcessor(SPARQL11Properties endpointProperties, EngineProperties engineProperties, Semaphore endpointSemaphore) { this.endpointProperties = endpointProperties; @@ -67,16 +59,16 @@ public SubscribeProcessor(SPARQL11Properties endpointProperties, EnginePropertie SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SubscribeProcessorBeans.setSPUProcessingTimeout(engineProperties.getSPUProcessingTimeout()); - this.subscriber = new Subscriber(subscribeQueue,spuManager); - this.unsubscriber = new Unsubcriber(unsubscribeQueue,spuManager); + this.subscriber = new Subscriber(spuManager); + this.unsubscriber = new Unsubscriber(spuManager); } - public void start(){ + public void start() { this.subscriber.start(); this.unsubscriber.start(); } - public void stop(){ + public void stop() { this.subscriber.finish(); this.unsubscriber.finish(); @@ -92,7 +84,7 @@ public Response subscribe(SubscribeRequest req, EventHandler handler) { // TODO: choose different kinds of SPU based on subscribe request SPU spu = null; try { - spu = new SPUNaive(req, handler, endpointProperties, endpointSemaphore, spuSync); + spu = new SPUNaive(req, handler, endpointProperties, endpointSemaphore, spuManager); } catch (SEPAProtocolException e) { logger.debug("SPU creation failed: " + e.getMessage()); @@ -107,7 +99,11 @@ public Response subscribe(SubscribeRequest req, EventHandler handler) { logger.debug("SPU initialization failed"); } else { logger.debug("Add SPU to activation queue"); - subscribeQueue.offer(spu); + try { + subscriber.activate(spu); + } catch (InterruptedException e) { + return new ErrorResponse(req.getToken(),500,"Failed activating SPU"); + } } return init; @@ -123,7 +119,11 @@ public Response unsubscribe(UnsubscribeRequest req) { if (!spuManager.isValidSpuId(spuid)) return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + spuid); - unsubscribeQueue.offer(spuid); + try { + unsubscriber.deactivate(spuid); + } catch (InterruptedException e) { + return new ErrorResponse(req.getToken(), 500, "Failed to unsubscribe: " + spuid); + } return new UnsubscribeResponse(req.getToken(), spuid); } @@ -134,17 +134,10 @@ public void process(UpdateResponse update) { logger.debug("Activate SPUs (Total: " + spuManager.size() + ")"); - spuSync.startProcessing(spuManager.getAll()); - - // TODO: filter algorithm - // Synchronize on spuManger to avoid changes of SPU collection - synchronized (spuManager) { - for (ISPU spu : spuManager.getAll()) - spu.process(update); - } + spuManager.startProcessing(update); // Wait all SPUs completing processing (or timeout) - spuSync.waitEndOfProcessing(); + spuManager.waitEndOfProcessing(); Instant stop = Instant.now(); SubscribeProcessorBeans.timings(start, stop); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java index 6b4ae3c7..070d4211 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -5,17 +5,22 @@ import org.apache.logging.log4j.Logger; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +//import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; public class Subscriber extends Thread { private final Logger logger = LogManager.getLogger(); private final AtomicBoolean end = new AtomicBoolean(false); - private final BlockingQueue subscriptionQueue; + private final BlockingQueue subscriptionQueue = new LinkedBlockingQueue(); private final SPUManager spuManager; - public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ + //private LinkedBlockingQueue subscribeQueue = new LinkedBlockingQueue<>(); + + //public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ + public Subscriber(SPUManager manager){ super("SEPA-SPU-Subscriber"); - this.subscriptionQueue = subscriptionQueue; + //this.subscriptionQueue = subscriptionQueue; spuManager = manager; } @@ -24,7 +29,9 @@ public void run() { while (!end.get()) { ISPU spu = null; try { + // Wait for a new SPU to be activated spu = subscriptionQueue.take(); + // Start the SPU thread Thread th = new Thread(spu); th.setName("SPU_" + spu.getUUID()); @@ -40,6 +47,10 @@ public void run() { } } + public void activate(ISPU spu) throws InterruptedException { + subscriptionQueue.put(spu); + } + public void finish(){ end.set(true); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java similarity index 76% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java index 9d11c9f7..2afa10ad 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubcriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java @@ -5,6 +5,7 @@ import org.apache.logging.log4j.Logger; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -13,15 +14,15 @@ * * @see SPUManager */ -public class Unsubcriber extends Thread { +public class Unsubscriber extends Thread { private final Logger logger = LogManager.getLogger(); - private final BlockingQueue unsubscribeQueue; + private final BlockingQueue unsubscribeQueue = new LinkedBlockingQueue(); private final SPUManager spuManager; private final AtomicBoolean end = new AtomicBoolean(false); - public Unsubcriber(BlockingQueue unsubscribeQueue, SPUManager manager){ + public Unsubscriber(SPUManager manager){ super("SEPA-SPU-Unsubscriber"); - this.unsubscribeQueue = unsubscribeQueue; + //this.unsubscribeQueue = unsubscribeQueue; spuManager = manager; } @@ -42,6 +43,10 @@ public void run() { } } } + + public void deactivate(String spuid) throws InterruptedException { + unsubscribeQueue.put(spuid); + } public void finish(){ end.set(true); diff --git a/tools/chat.jsap b/tools/chat.jsap index d9a5c3d0..816c7c68 100644 --- a/tools/chat.jsap +++ b/tools/chat.jsap @@ -1,5 +1,10 @@ { "host": "localhost", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, "sparql11protocol": { "protocol": "http", "port": 8000, @@ -25,10 +30,6 @@ "port": 9443, "path": "/secure/subscribe" } - }, - "security": { - "register": "http://localhost:8080/oauth/register", - "tokenRequest": "http://localhost:8080/oauth/token" } }, "extended": { From 915760082ca1b3407d478007070c1c592886295d Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 5 Jul 2018 11:27:12 +0200 Subject: [PATCH 27/76] Modify is*Response to use instanceof Class equal doesn't check for subclass instances --- .../wot/sepa/commons/response/Response.java | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java index a4e121d4..79abdc64 100644 --- a/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java +++ b/commons/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java @@ -33,31 +33,31 @@ public abstract class Response { private int token = -1; public boolean isError() { - return this.getClass().equals(ErrorResponse.class); + return this instanceof ErrorResponse; } public boolean isJWTResponse() { - return this.getClass().equals(JWTResponse.class); + return this instanceof JWTResponse; } public boolean isNotification() { - return this.getClass().equals(Notification.class); + return this instanceof Notification; } public boolean isPing() { - return this.getClass().equals(Ping.class); + return this instanceof Ping; } public boolean isQueryResponse() { - return this.getClass().equals(QueryResponse.class); + return this instanceof QueryResponse; } public boolean isRegistrationResponse() { - return this.getClass().equals(RegistrationResponse.class); + return this instanceof RegistrationResponse; } public boolean isSubscribeResponse() { - return this.getClass().equals(SubscribeResponse.class); + return this instanceof SubscribeResponse; } public boolean isUnsubscribeResponse() { - return this.getClass().equals(UnsubscribeResponse.class); + return this instanceof UnsubscribeResponse; } public boolean isUpdateResponse() { - return this.getClass().equals(UpdateResponse.class); + return this instanceof UpdateResponse; } /** * Instantiates a new response. @@ -92,13 +92,4 @@ public String toString() { public int getToken() { return token; } - - /** - * Gets the as json object. - * - * @return the as json object - */ - /*public JsonObject getAsJsonObject(){ - return json; - }*/ } From 14bb680b788187578e50947e1e4ea5d72bb4d094 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 5 Jul 2018 11:30:00 +0200 Subject: [PATCH 28/76] Add Added and Removed processing With this commit the UpdateProcessor check which are the added and removed triples. This is the first implementation and it does not handle update request for multiple graph. It also doesn't check if the triple is ACTUALLY added or remove. --- engine/pom.xml | 6 +- .../engine/processing/SPARQLAnalyzer.java | 265 ++++++++++++++++++ .../engine/processing/UpdateConstruct.java | 34 +++ .../engine/processing/UpdateProcessor.java | 54 +++- .../processing/UpdateResponseWithAR.java | 30 ++ .../java/engine/ConfigurationProvider.java | 23 ++ .../test/java/engine/ITUpdateProcessor.java | 64 +++++ .../engine/processing/SPARQLAnalyzerTest.java | 55 ++++ .../SPUMangerTest.java | 4 +- engine/src/test/resources/dev.jsap | 46 +++ 10 files changed, 573 insertions(+), 8 deletions(-) create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java create mode 100644 engine/src/test/java/engine/ConfigurationProvider.java create mode 100644 engine/src/test/java/engine/ITUpdateProcessor.java create mode 100644 engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java rename engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/{subscription => subscriptions}/SPUMangerTest.java (89%) create mode 100644 engine/src/test/resources/dev.jsap diff --git a/engine/pom.xml b/engine/pom.xml index beaba1a8..d92c311b 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -164,7 +164,11 @@ client-api ${revision} - + + org.apache.jena + jena-arq + 3.4.0 + junit junit diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java new file mode 100644 index 00000000..2e8dfa2c --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java @@ -0,0 +1,265 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import org.apache.jena.atlas.lib.Sink; +import org.apache.jena.graph.Triple; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.query.Syntax; +import org.apache.jena.sparql.algebra.*; +import org.apache.jena.sparql.algebra.op.OpBGP; +import org.apache.jena.sparql.core.BasicPattern; +import org.apache.jena.sparql.core.Quad; +import org.apache.jena.sparql.core.TriplePath; +import org.apache.jena.sparql.lang.UpdateParserFactory; +import org.apache.jena.sparql.lang.arq.ParseException; +import org.apache.jena.sparql.modify.request.*; +import org.apache.jena.sparql.syntax.*; +import org.apache.jena.update.Update; +import org.apache.jena.update.UpdateFactory; +import org.apache.jena.update.UpdateRequest; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class SPARQLAnalyzer { + + String test = null; + + public void setString(String s) { + test = s; + } + + class MyTransform extends TransformCopy + { + @Override + public Op transform(OpBGP opBGP) + { + // create a new construct query + Query q = QueryFactory.make(); + q.setQueryConstructType(); + + // parse the bgp + BasicPattern b = opBGP.getPattern(); + Iterator opIterator = b.iterator(); + Template ttt = new Template(b); + q.setConstructTemplate(ttt); + ElementGroup body = new ElementGroup(); + ElementUnion union = new ElementUnion(); + + while (opIterator.hasNext()){ + Triple bb = opIterator.next(); + + // for the query + ElementTriplesBlock block = new ElementTriplesBlock(); // Make a BGP + block.addTriple(bb); + body.addElement(block); + logger.debug(bb.toString()); + + // union + union.addElement(block); + + } + + q.setQueryPattern(body); + q.setQueryPattern(union); + + setString(q.toString()); + logger.debug(q.toString()); + + return opBGP; + } + } + + class ToConstructUpdateVisitor extends UpdateVisitorBase{ + private UpdateConstruct result = new UpdateConstruct("",""); + @Override + public void visit(UpdateDataInsert updateDataInsert) { + Query insertQuery = createBaseConstruct(new QuadAcc(updateDataInsert.getQuads())); + String insertString = insertQuery.isUnknownType() ? "" : insertQuery.serialize() + "WHERE{}"; + result = new UpdateConstruct("",insertString); + } + + + + @Override + public void visit(UpdateDataDelete updateDataDelete) { + Query deleteQuery = createBaseConstruct(new QuadAcc(updateDataDelete.getQuads())); + String deleteString = deleteQuery.isUnknownType() ? "" : deleteQuery.serialize()+"WHERE{}"; + result = new UpdateConstruct(deleteString,""); + } + + @Override + public void visit(UpdateDeleteWhere updateDeleteWhere) { + Query updateDeleteQuery = createBaseConstruct(new QuadAcc(updateDeleteWhere.getQuads())); + if(!updateDeleteQuery.isUnknownType()) { + ElementGroup where = new ElementGroup(); + for (Quad q : updateDeleteWhere.getQuads()) { + where.addTriplePattern(q.asTriple()); + } + updateDeleteQuery.setQueryPattern(where); + result = new UpdateConstruct(updateDeleteQuery.serialize(), ""); + } + } + + @Override + public void visit(UpdateModify updateModify) { + String insertString = ""; + String deleteString = ""; + + if(updateModify.hasDeleteClause() && !updateModify.getDeleteAcc().getQuads().isEmpty()){ + Template constructDelete = new Template(updateModify.getDeleteAcc()); + Query constructQueryDelete = new Query(); + constructQueryDelete.setQueryConstructType(); + constructQueryDelete.setConstructTemplate(constructDelete); + constructQueryDelete.setQueryPattern(updateModify.getWherePattern()); + deleteString = constructQueryDelete.toString(); + } + + if(updateModify.hasInsertClause() && !updateModify.getInsertAcc().getQuads().isEmpty()){ + Template constructInsert = new Template(updateModify.getInsertAcc()); + Query constructQueryInsert = new Query(); + constructQueryInsert.setQueryConstructType(); + constructQueryInsert.setConstructTemplate(constructInsert); + constructQueryInsert.setQueryPattern(updateModify.getWherePattern()); + insertString = constructQueryInsert.serialize(); + } + + result = new UpdateConstruct(deleteString,insertString); + + } + + @Override + public void visit(UpdateClear update) { + String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getGraph().getURI()+"> { ?s ?p ?o } . }"; + result = new UpdateConstruct(deleteConstruct,""); + } + + @Override + public void visit(UpdateDrop update) { + String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getGraph().getURI()+"> { ?s ?p ?o } . }"; + result = new UpdateConstruct(deleteConstruct,""); + } + + @Override + public void visit(UpdateCopy update) { + String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getDest().getGraph().getURI()+"> { ?s ?p ?o } . }"; + String insertConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getSrc().getGraph().getURI()+"> { ?s ?p ?o } . }"; + result = new UpdateConstruct(deleteConstruct,insertConstruct); + } + + @Override + public void visit(UpdateAdd update) { + String insertConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getDest().getGraph().getURI()+"> { ?s ?p ?o } . }"; + result = new UpdateConstruct("",insertConstruct); + } + + //TODO: Move + + public UpdateConstruct getResult() { + return result; + } + + private Query createBaseConstruct( QuadAcc quads) { + Query result = new Query(); + if(!quads.getQuads().isEmpty()){ + Template construct = new Template(quads); + result = new Query(); + result.setQueryConstructType(); + result.setConstructTemplate(construct); + } + return result; + } + + + } + + // attributes + private String sparqlText; + private final static Logger logger = LogManager.getLogger("SPARQLAnalyzer"); + + // Constructor + SPARQLAnalyzer(String request){ + // store the query text + sparqlText = request; + } + + + UpdateConstruct getConstruct() { + UpdateRequest updates = UpdateFactory.create(sparqlText); + for(Update up : updates){ + ToConstructUpdateVisitor updateVisitor = new ToConstructUpdateVisitor(); + up.visit(updateVisitor); + return updateVisitor.getResult(); + } + throw new IllegalArgumentException("No valid operation found"); + } + + // LUTT generator + List getLutt(){ + + // debug print + logger.debug("Analyzing query " + sparqlText); + + // create a variable for the LUTT + List lutt = new ArrayList(); + + // extract basic graph patterns + Query q = QueryFactory.create(sparqlText); + Element e = q.getQueryPattern(); + + // This will walk through all parts of the query + ElementWalker.walk(e, + + // For each element... + new ElementVisitorBase() { + + // ...when it's a block of triples... + public void visit(ElementPathBlock el) { + + // ...go through all the triples... + Iterator triples = el.patternElts(); + while (triples.hasNext()) { + + // get the current triple pattern + TriplePath t = triples.next(); + lutt.add(t); + + // debug print + logger.debug("Found Triple Pattern: " + t.getSubject() + " " + t.getPredicate() + " " + t.getObject()); } + } + } + ); + + // return! + return lutt; + } + + // Construct Generator + String getConstructFromQuery() throws ParseException { + + // This method allows to derive the CONSTRUCT query + // from the SPARQL SUBSCRIPTION + + // get the algebra from the query + + Query qqq = QueryFactory.create(sparqlText, Syntax.syntaxSPARQL); + Op op = Algebra.compile(qqq); + + // get the algebra version of the construct query and + // convert it back to query + Transform transform = new MyTransform() ; + op = Transformer.transform(transform, op) ; + Query q = OpAsQuery.asQuery(op); + // return + return test; + + } + +} \ No newline at end of file diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java new file mode 100644 index 00000000..f3691335 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java @@ -0,0 +1,34 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +public class UpdateConstruct { + + private final String deleteConstruct; + private final String insertConstruct; + + UpdateConstruct(String deleteConstruct, String insertConstruct){ + if(deleteConstruct == null || insertConstruct == null){ + throw new IllegalArgumentException("Construct query cannot be null"); + } + + this.deleteConstruct = deleteConstruct; + this.insertConstruct = insertConstruct; + } + + /** + * Get delete construct string. An empty string indicates that there are no deleted + * triples + * @return a construct sparql query string + */ + public String getInsertConstruct() { + return insertConstruct; + } + + /** + * Get delete construct string. An empty string indicates that there are no deleted + * triples + * @return a construct sparql query string + */ + public String getDeleteConstruct() { + return deleteConstruct; + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index bcae302f..49219c58 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -19,8 +19,18 @@ package it.unibo.arces.wot.sepa.engine.processing; import java.time.Instant; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.concurrent.Semaphore; +import com.google.gson.JsonObject; +import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.response.QueryResponse; +import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; +import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; +import org.apache.jena.update.Update; +import org.apache.jena.update.UpdateFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -53,13 +63,39 @@ public synchronized Response process(UpdateRequest req, int timeout) { } catch (InterruptedException e) { return new ErrorResponse(500,e.getMessage()); } - + + // Get the constructs needed to determine added and removed data + + long start = System.currentTimeMillis(); + SPARQLAnalyzer sa = new SPARQLAnalyzer(req.getSPARQL()); + UpdateConstruct constructs = sa.getConstruct(); + + + BindingsResults added = new BindingsResults(new JsonObject()); + BindingsResults removed = new BindingsResults(new JsonObject()); + + String dc = constructs.getDeleteConstruct(); + + if (dc.length() > 0) { + removed = getTriples(timeout, dc); + } + + String ac = constructs.getInsertConstruct(); + if (ac.length() > 0) { + added = getTriples(timeout, ac); + } + + long stop = System.currentTimeMillis(); + logger.debug("* ADDED REMOVED PROCESSING ("+(stop-start)+" ms) *"); + + ProcessorBeans.updateTimings(start, stop); + // UPDATE the endpoint - long start = System.currentTimeMillis(); + start = System.currentTimeMillis(); Timing.logTiming(req, "ENDPOINT_REQUEST", Instant.now()); Response ret = endpoint.update(req, timeout); Timing.logTiming(req, "ENDPOINT_RESPONSE", Instant.now()); - long stop = System.currentTimeMillis(); + stop = System.currentTimeMillis(); if (endpointSemaphore != null) endpointSemaphore.release(); @@ -67,7 +103,17 @@ public synchronized Response process(UpdateRequest req, int timeout) { logger.debug("* UPDATE PROCESSING ("+(stop-start)+" ms) *"); ProcessorBeans.updateTimings(start, stop); - + + ret = ret.isUpdateResponse() ? new UpdateResponseWithAR((UpdateResponse) ret,added,removed) : ret; return ret; } + + private BindingsResults getTriples(int timeout, String dc) { + BindingsResults removed; + QueryRequest cons1 = new QueryRequest(dc); + logger.debug(cons1.toString()); + removed = ((QueryResponse) endpoint.query(cons1, timeout)).getBindingsResults(); + logger.debug(removed); + return removed; + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java new file mode 100644 index 00000000..8073434e --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java @@ -0,0 +1,30 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; +import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; + +/** + * An update response with Added and Removed triples. + */ +public class UpdateResponseWithAR extends UpdateResponse { + + private final BindingsResults added; + private final BindingsResults removed; + + public UpdateResponseWithAR(UpdateResponse ret, BindingsResults added, BindingsResults removed) { + super(ret); + if(added == null || removed == null){ + throw new IllegalArgumentException("Bindings cannot be null"); + } + this.added = added; + this.removed = removed; + } + + public BindingsResults getRemoved() { + return removed; + } + + public BindingsResults getAdded() { + return added; + } +} diff --git a/engine/src/test/java/engine/ConfigurationProvider.java b/engine/src/test/java/engine/ConfigurationProvider.java new file mode 100644 index 00000000..a50a2815 --- /dev/null +++ b/engine/src/test/java/engine/ConfigurationProvider.java @@ -0,0 +1,23 @@ +package engine; + +import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; + +import java.io.File; +import java.net.URL; + +public class ConfigurationProvider { + + public static SPARQL11SEProperties GetTestEnvConfiguration() throws SEPAPropertiesException { + SPARQL11SEProperties result; + final String configuaration = System.getProperty("testConfiguration"); + if( configuaration != null){ + final File confFile = new File(configuaration); + result = new SPARQL11SEProperties(confFile); + }else{ + URL config = Thread.currentThread().getContextClassLoader().getResource("dev.jsap"); + result = new SPARQL11SEProperties(new File(config.getPath())); + } + return result; + } +} diff --git a/engine/src/test/java/engine/ITUpdateProcessor.java b/engine/src/test/java/engine/ITUpdateProcessor.java new file mode 100644 index 00000000..5fde0610 --- /dev/null +++ b/engine/src/test/java/engine/ITUpdateProcessor.java @@ -0,0 +1,64 @@ +package engine; + +import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.sparql.Bindings; +import it.unibo.arces.wot.sepa.engine.processing.UpdateProcessor; +import it.unibo.arces.wot.sepa.engine.processing.UpdateResponseWithAR; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class ITUpdateProcessor { + + private UpdateProcessor updateProcessor; + + @Before + public void init() throws SEPAPropertiesException, SEPAProtocolException { + final SPARQL11SEProperties sparql11SEProperties = ConfigurationProvider.GetTestEnvConfiguration(); + updateProcessor = new UpdateProcessor(sparql11SEProperties, null); + } + @Test + public void testInsertAddRemoved(){ + UpdateRequest updateRequest = new UpdateRequest("INSERT{ }Where{}"); + Response process = updateProcessor.process(updateRequest, 5000); + + assertTrue("Not a UpdateResponse",process instanceof UpdateResponseWithAR); + UpdateResponseWithAR uAR = (UpdateResponseWithAR) process; + + assertTrue("Added is empty",!uAR.getAdded().isEmpty()); + assertTrue("Removed is not empty",uAR.getRemoved().isEmpty()); + + String sub = uAR.getAdded().getBindings().get(0).getBindingValue("subject"); + String pred = uAR.getAdded().getBindings().get(0).getBindingValue("predicate"); + String obj = uAR.getAdded().getBindings().get(0).getBindingValue("object"); + + assertEquals("test://it/update",sub); + assertEquals("test://it/update/pre",pred); + assertEquals("test://it/update/obj",obj); + } + + @Test + public void testDeleteAddRemoved(){ + UpdateRequest updateRequest = new UpdateRequest("DELETE{ }Where{}"); + Response process = updateProcessor.process(updateRequest, 5000); + + assertTrue("Not a UpdateResponse",process instanceof UpdateResponseWithAR); + UpdateResponseWithAR uAR = (UpdateResponseWithAR) process; + + assertTrue("Removed is empty",!uAR.getRemoved().isEmpty()); + assertTrue("Added is not empty",uAR.getAdded().isEmpty()); + + String sub = uAR.getRemoved().getBindings().get(0).getBindingValue("subject"); + String pred = uAR.getRemoved().getBindings().get(0).getBindingValue("predicate"); + String obj = uAR.getRemoved().getBindings().get(0).getBindingValue("object"); + + assertEquals("test://it/update",sub); + assertEquals("test://it/update/pre",pred); + assertEquals("test://it/update/obj",obj); + } +} diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java new file mode 100644 index 00000000..dc057360 --- /dev/null +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java @@ -0,0 +1,55 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import org.apache.jena.sparql.lang.arq.ParseException; +import org.apache.jena.update.Update; +import org.apache.jena.update.UpdateFactory; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import static org.junit.Assert.*; + +public class SPARQLAnalyzerTest { + private static final int DELETE_OUT = 1; + private static final int INSERT_OUT = 2; + private static final int INPUT = 0; + + private String [][] io = new String[][]{ + {"INSERT { }WHERE{}","","CONSTRUCT{.}WHERE{}"}, + {"DELETE WHERE{?a ?c}","CONSTRUCT{?a?c.}WHERE{?a?c}",""}, + {"CLEAR GRAPH ","CONSTRUCT{?s?p?o}WHERE{GRAPH{?s?p?o}.}",""}, + {"DELETE { }WHERE{?a ?b ?c}","CONSTRUCT{.}WHERE{?a?b?c}",""}, + {"DELETE { } INSERT { }WHERE{}","CONSTRUCT{.}WHERE{}","CONSTRUCT{.}WHERE{}"}, + {"DELETE { } INSERT { }WHERE{?a ?b ?c}","CONSTRUCT{.}WHERE{?a?b?c}","CONSTRUCT{.}WHERE{?a?b?c}"}, + {"INSERT DATA{ . }","","CONSTRUCT{..}WHERE{}"}, + {"DELETE DATA{ . }","CONSTRUCT{..}WHERE{}",""}, + }; + +//TODO Test move copy add operations + + @Test + public void setString() { + } + + @Test + public void getConstruct() { + for(String[] test : io){ + SPARQLAnalyzer sparqlAnalyzer = new SPARQLAnalyzer(test[INPUT]); + + UpdateConstruct construct = sparqlAnalyzer.getConstruct(); + assertEquals(test[DELETE_OUT], construct.getDeleteConstruct().replaceAll("\\s+","")); + + assertEquals(test[INSERT_OUT], construct.getInsertConstruct().replaceAll("\\s+","")); + } + } + + @Test + public void getLutt() { + } + + @Test + public void getConstructFromQuery() { + } +} \ No newline at end of file diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUMangerTest.java similarity index 89% rename from engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java rename to engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUMangerTest.java index c4c66495..9f97aba1 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUMangerTest.java @@ -1,10 +1,8 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscription; +package it.unibo.arces.wot.sepa.engine.processing.subscriptions; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISubscriptionProcUnit; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SpuManager; import org.junit.Assert; import org.junit.Before; import org.junit.Test; diff --git a/engine/src/test/resources/dev.jsap b/engine/src/test/resources/dev.jsap new file mode 100644 index 00000000..1266f08f --- /dev/null +++ b/engine/src/test/resources/dev.jsap @@ -0,0 +1,46 @@ +{ + "host": "localhost", + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/subscribe" + } + }, + "security": { + "register": "/oauth/register", + "tokenRequest": "/oauth/token", + "securePath": "/secure", + "client_id": "ryd7aRCosLlFYowQ5jid0lOlEdMhmbBFFCRZy6fA8KZtsQnuqhfg1zXbzCxpNPih", + "client_secret": "N5UgtT0EWMYV3aIsC+iyAB5kk2maR8MrEXV1+NaEocLFz4eYeVT1mKoRIDwgbpsJ", + "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfsRgSntEVGcQQR//AJ7psDRK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmjDdzXXWr/jf1HAP4lTU6IeQqN57UDzW9Mr4Y/+5CnidJI5/KxklwtkuhlzeMQupyx34x2j1BAIJLuPIZHnU1oufaKo3I4b6us1FC0JxtftwpFmv5bnXCHKhsygqwQiqSoS6//kP9HugQjTW7b7kFcUsSriJhJNxztijHcYC1YSpLts3699qLcVmlh0+rF5lDncLv15C4CrA4Ro5BGp6AoAChOSB9WFxmivcFefl9V01XlsXMV64aVHzjFMWWXeLR0TXTDIih8LRJKFcAmM4M6ULqqyl9ELprG9axFSN2GINfkTopEKcGeCWEwJRbHxzor7qgxAzafboWDRb1J0DQxvTNuzK47b9i8QGRiWVkiS6rGnAArdIIiLLPzqOfR0/aGwuFrHfGoxxrJJvatKH9+IwZKW/lSg5XU23MSqKrLALOCInXDVMiK25w0nAff4P0hAnCEkUzkntRbGTrcPuUV8j298e7/NmelTlHqw8xZMf+0iZMBQrgzuXWni4Ok9+bMu65HZ4pBHEBJz5296s093a9/GHcRZaL6jUwd31/MD08PdBu6SYUVS7YKqND6U1jKt9w25i4xnOoJ9sBPcIMDCmgia/gqjR8kaTSgABEogibvwQvDnLpgcHKkh7U5lRegzxeDVO1V63Xj1tJgeGbB", + "expires": "QWAE7shP6t6lAuzmw5A8ZQ==", + "type": "XPrHEX2xHy+5IuXHPHigMw==" + } + }, + "graphs": { + "default-graph-uri": "http://default", + "named-graph-uri": "http://default", + "using-graph-uri": "http://default", + "using-named-graph-uri": "http://default" + } +} \ No newline at end of file From 00205d7dfede43754133e4a8d4d519fe1a4443a8 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 5 Jul 2018 11:52:19 +0200 Subject: [PATCH 29/76] Untrack timing logs --- .gitignore | 3 + engine/logs/20180411_12_53_47.csv | 372 ------------------------------ engine/logs/20180412_13_45_37.csv | 85 ------- engine/logs/20180412_13_55_07.csv | 27 --- engine/logs/20180412_13_59_52.csv | 0 engine/logs/20180412_14_00_13.csv | 0 engine/logs/20180412_14_01_11.csv | 0 engine/logs/20180412_14_01_21.csv | 0 engine/logs/20180412_14_03_44.csv | 0 engine/logs/20180419_09_42_39.csv | 0 10 files changed, 3 insertions(+), 484 deletions(-) delete mode 100644 engine/logs/20180411_12_53_47.csv delete mode 100644 engine/logs/20180412_13_45_37.csv delete mode 100644 engine/logs/20180412_13_55_07.csv delete mode 100644 engine/logs/20180412_13_59_52.csv delete mode 100644 engine/logs/20180412_14_00_13.csv delete mode 100644 engine/logs/20180412_14_01_11.csv delete mode 100644 engine/logs/20180412_14_01_21.csv delete mode 100644 engine/logs/20180412_14_03_44.csv delete mode 100644 engine/logs/20180419_09_42_39.csv diff --git a/.gitignore b/.gitignore index a9769d51..2cf444de 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ engine/*.jpar engine/sepa.jks +# timing created by engine +engine/logs + # Created by https://www.gitignore.io/api/java,maven,eclipse,intellij ### Eclipse ### diff --git a/engine/logs/20180411_12_53_47.csv b/engine/logs/20180411_12_53_47.csv deleted file mode 100644 index 96109542..00000000 --- a/engine/logs/20180411_12_53_47.csv +++ /dev/null @@ -1,372 +0,0 @@ - -UPDATE -1 REQUEST 1523455100078 -UPDATE -1 SCHEDULING 1523455100079 -UPDATE 36 SCHEDULED 1523455100079 -UPDATE 36 ENDPOINT_REQUEST 1523455100079 -UPDATE 36 ENDPOINT_RESPONSE 1523455100182 -UPDATE 36 RESPONDING 1523455100182 -UPDATE 36 RESPONSE_SENT 1523455100183 -UPDATE -1 REQUEST 1523455100199 -UPDATE -1 SCHEDULING 1523455100200 -UPDATE 37 SCHEDULED 1523455100200 -UPDATE 37 ENDPOINT_REQUEST 1523455100200 -UPDATE 37 ENDPOINT_RESPONSE 1523455100290 -UPDATE 37 RESPONDING 1523455100291 -UPDATE 37 RESPONSE_SENT 1523455100291 -UPDATE -1 REQUEST 1523455100293 -UPDATE -1 SCHEDULING 1523455100293 -UPDATE 38 SCHEDULED 1523455100293 -UPDATE 38 ENDPOINT_REQUEST 1523455100293 -UPDATE 38 ENDPOINT_RESPONSE 1523455100387 -UPDATE 38 RESPONDING 1523455100388 -UPDATE 38 RESPONSE_SENT 1523455100388 -UPDATE -1 REQUEST 1523455100390 -UPDATE -1 SCHEDULING 1523455100390 -UPDATE 39 SCHEDULED 1523455100390 -UPDATE 39 ENDPOINT_REQUEST 1523455100390 -UPDATE 39 ENDPOINT_RESPONSE 1523455100783 -UPDATE 39 RESPONDING 1523455100783 -UPDATE 39 RESPONSE_SENT 1523455100783 -UPDATE -1 REQUEST 1523455100785 -UPDATE -1 SCHEDULING 1523455100785 -UPDATE 40 SCHEDULED 1523455100785 -UPDATE 40 ENDPOINT_REQUEST 1523455100785 -UPDATE 40 ENDPOINT_RESPONSE 1523455100911 -UPDATE 40 RESPONDING 1523455100912 -UPDATE 40 RESPONSE_SENT 1523455100912 -UPDATE -1 REQUEST 1523455100913 -UPDATE -1 SCHEDULING 1523455100914 -UPDATE 41 SCHEDULED 1523455100914 -UPDATE 41 ENDPOINT_REQUEST 1523455100914 -UPDATE 41 ENDPOINT_RESPONSE 1523455101351 -UPDATE 41 RESPONDING 1523455101352 -UPDATE 41 RESPONSE_SENT 1523455101352 -UPDATE -1 REQUEST 1523455101355 -UPDATE -1 SCHEDULING 1523455101356 -UPDATE 42 SCHEDULED 1523455101356 -UPDATE 42 ENDPOINT_REQUEST 1523455101356 -UPDATE 42 ENDPOINT_RESPONSE 1523455101468 -UPDATE 42 RESPONDING 1523455101468 -UPDATE 42 RESPONSE_SENT 1523455101468 -UPDATE -1 REQUEST 1523455101471 -UPDATE -1 SCHEDULING 1523455101471 -UPDATE 43 ENDPOINT_REQUEST 1523455101471 -UPDATE 43 SCHEDULED 1523455101471 -UPDATE 43 ENDPOINT_RESPONSE 1523455101527 -UPDATE 43 RESPONDING 1523455101528 -UPDATE 43 RESPONSE_SENT 1523455101528 -UPDATE -1 REQUEST 1523455101530 -UPDATE -1 SCHEDULING 1523455101530 -UPDATE 44 SCHEDULED 1523455101531 -UPDATE 44 ENDPOINT_REQUEST 1523455101531 -UPDATE 44 ENDPOINT_RESPONSE 1523455101618 -UPDATE 44 RESPONDING 1523455101618 -UPDATE 44 RESPONSE_SENT 1523455101618 -UPDATE -1 REQUEST 1523455101620 -UPDATE -1 SCHEDULING 1523455101620 -UPDATE 45 SCHEDULED 1523455101621 -UPDATE 45 ENDPOINT_REQUEST 1523455101621 -UPDATE 45 ENDPOINT_RESPONSE 1523455101680 -UPDATE 45 RESPONDING 1523455101680 -UPDATE 45 RESPONSE_SENT 1523455101681 -UPDATE -1 REQUEST 1523455101686 -UPDATE -1 SCHEDULING 1523455101687 -UPDATE 46 SCHEDULED 1523455101687 -UPDATE 46 ENDPOINT_REQUEST 1523455101687 -UPDATE 46 ENDPOINT_RESPONSE 1523455101788 -UPDATE 46 RESPONDING 1523455101788 -UPDATE 46 RESPONSE_SENT 1523455101789 -QUERY -1 REQUEST 1523455101791 -QUERY -1 SCHEDULING 1523455101791 -QUERY 47 SCHEDULED 1523455101791 -QUERY 47 ENDPOINT_REQUEST 1523455101792 -QUERY 47 ENDPOINT_RESPONSE 1523455101859 -QUERY 47 RESPONDING 1523455101859 -QUERY 47 RESPONSE_SENT 1523455101859 -UPDATE -1 REQUEST 1523455101865 -UPDATE -1 SCHEDULING 1523455101866 -UPDATE 48 SCHEDULED 1523455101866 -UPDATE 48 ENDPOINT_REQUEST 1523455101866 -UPDATE 48 ENDPOINT_RESPONSE 1523455101926 -UPDATE 48 RESPONDING 1523455101927 -UPDATE 48 RESPONSE_SENT 1523455101927 -UPDATE -1 REQUEST 1523455101929 -UPDATE -1 SCHEDULING 1523455101929 -UPDATE 49 SCHEDULED 1523455101929 -UPDATE 49 ENDPOINT_REQUEST 1523455101929 -UPDATE 49 ENDPOINT_RESPONSE 1523455102027 -UPDATE 49 RESPONDING 1523455102027 -UPDATE 49 RESPONSE_SENT 1523455102027 -UPDATE -1 REQUEST 1523455102029 -UPDATE -1 SCHEDULING 1523455102029 -UPDATE 50 SCHEDULED 1523455102030 -UPDATE 50 ENDPOINT_REQUEST 1523455102030 -UPDATE 50 ENDPOINT_RESPONSE 1523455102085 -UPDATE 50 RESPONDING 1523455102085 -UPDATE 50 RESPONSE_SENT 1523455102086 -UPDATE -1 REQUEST 1523455102089 -UPDATE -1 SCHEDULING 1523455102089 -UPDATE 51 ENDPOINT_REQUEST 1523455102090 -UPDATE 51 SCHEDULED 1523455102089 -UPDATE 51 ENDPOINT_RESPONSE 1523455102173 -UPDATE 51 RESPONDING 1523455102173 -UPDATE 51 RESPONSE_SENT 1523455102173 -UPDATE -1 REQUEST 1523455102175 -UPDATE -1 SCHEDULING 1523455102175 -UPDATE 52 SCHEDULED 1523455102175 -UPDATE 52 ENDPOINT_REQUEST 1523455102175 -UPDATE 52 ENDPOINT_RESPONSE 1523455102316 -UPDATE 52 RESPONDING 1523455102316 -UPDATE 52 RESPONSE_SENT 1523455102317 -UPDATE -1 REQUEST 1523455102319 -UPDATE -1 SCHEDULING 1523455102319 -UPDATE 53 SCHEDULED 1523455102320 -UPDATE 53 ENDPOINT_REQUEST 1523455102320 -UPDATE 53 ENDPOINT_RESPONSE 1523455102458 -UPDATE 53 RESPONDING 1523455102459 -UPDATE 53 RESPONSE_SENT 1523455102459 -UPDATE -1 REQUEST 1523455102462 -UPDATE -1 SCHEDULING 1523455102463 -UPDATE 54 SCHEDULED 1523455102463 -UPDATE 54 ENDPOINT_REQUEST 1523455102463 -UPDATE 54 ENDPOINT_RESPONSE 1523455102504 -UPDATE 54 RESPONDING 1523455102505 -UPDATE 54 RESPONSE_SENT 1523455102505 -UPDATE -1 REQUEST 1523455102507 -UPDATE -1 SCHEDULING 1523455102508 -UPDATE 55 ENDPOINT_REQUEST 1523455102508 -UPDATE 55 SCHEDULED 1523455102508 -UPDATE 55 ENDPOINT_RESPONSE 1523455102613 -UPDATE 55 RESPONDING 1523455102614 -UPDATE 55 RESPONSE_SENT 1523455102614 -UPDATE -1 REQUEST 1523455102617 -UPDATE -1 SCHEDULING 1523455102618 -UPDATE 56 SCHEDULED 1523455102618 -UPDATE 56 ENDPOINT_REQUEST 1523455102618 -UPDATE 56 ENDPOINT_RESPONSE 1523455102694 -UPDATE 56 RESPONDING 1523455102695 -UPDATE 56 RESPONSE_SENT 1523455102695 -UPDATE -1 REQUEST 1523455102697 -UPDATE -1 SCHEDULING 1523455102698 -UPDATE 57 SCHEDULED 1523455102698 -UPDATE 57 ENDPOINT_REQUEST 1523455102698 -UPDATE 57 ENDPOINT_RESPONSE 1523455102753 -UPDATE 57 RESPONDING 1523455102754 -UPDATE 57 RESPONSE_SENT 1523455102754 -QUERY -1 REQUEST 1523455102757 -QUERY -1 SCHEDULING 1523455102757 -QUERY 58 SCHEDULED 1523455102757 -QUERY 58 ENDPOINT_REQUEST 1523455102758 -QUERY 58 ENDPOINT_RESPONSE 1523455102881 -QUERY 58 RESPONDING 1523455102881 -QUERY 58 RESPONSE_SENT 1523455102882 -QUERY -1 REQUEST 1523455102883 -QUERY -1 SCHEDULING 1523455102884 -QUERY 59 SCHEDULED 1523455102884 -QUERY 59 ENDPOINT_REQUEST 1523455102884 -QUERY 59 ENDPOINT_RESPONSE 1523455103001 -QUERY 59 RESPONDING 1523455103001 -QUERY 59 RESPONSE_SENT 1523455103002 -QUERY -1 REQUEST 1523455103003 -QUERY -1 SCHEDULING 1523455103004 -QUERY 60 SCHEDULED 1523455103004 -QUERY 60 ENDPOINT_REQUEST 1523455103004 -QUERY 60 ENDPOINT_RESPONSE 1523455103079 -QUERY 60 RESPONDING 1523455103079 -QUERY 60 RESPONSE_SENT 1523455103080 -QUERY -1 REQUEST 1523455103081 -QUERY -1 SCHEDULING 1523455103082 -QUERY 61 SCHEDULED 1523455103082 -QUERY 61 ENDPOINT_REQUEST 1523455103082 -QUERY 61 ENDPOINT_RESPONSE 1523455103171 -QUERY 61 RESPONDING 1523455103171 -QUERY 61 RESPONSE_SENT 1523455103171 -QUERY -1 REQUEST 1523455103173 -QUERY -1 SCHEDULING 1523455103173 -QUERY 62 SCHEDULED 1523455103173 -QUERY 62 ENDPOINT_REQUEST 1523455103174 -QUERY 62 ENDPOINT_RESPONSE 1523455103252 -QUERY 62 RESPONDING 1523455103252 -QUERY 62 RESPONSE_SENT 1523455103252 -QUERY -1 REQUEST 1523455103254 -QUERY -1 SCHEDULING 1523455103254 -QUERY 63 SCHEDULED 1523455103254 -QUERY 63 ENDPOINT_REQUEST 1523455103255 -QUERY 63 ENDPOINT_RESPONSE 1523455103315 -QUERY 63 RESPONDING 1523455103315 -QUERY 63 RESPONSE_SENT 1523455103315 -QUERY -1 REQUEST 1523455103317 -QUERY -1 SCHEDULING 1523455103318 -QUERY 64 SCHEDULED 1523455103318 -QUERY 64 ENDPOINT_REQUEST 1523455103318 -QUERY 64 ENDPOINT_RESPONSE 1523455103401 -QUERY 64 RESPONDING 1523455103402 -QUERY 64 RESPONSE_SENT 1523455103403 -QUERY -1 REQUEST 1523455103405 -QUERY -1 SCHEDULING 1523455103406 -QUERY 65 SCHEDULED 1523455103406 -QUERY 65 ENDPOINT_REQUEST 1523455103407 -QUERY 65 ENDPOINT_RESPONSE 1523455103503 -QUERY 65 RESPONDING 1523455103504 -QUERY 65 RESPONSE_SENT 1523455103504 -QUERY -1 REQUEST 1523455103505 -QUERY -1 SCHEDULING 1523455103506 -QUERY 66 SCHEDULED 1523455103506 -QUERY 66 ENDPOINT_REQUEST 1523455103506 -QUERY 66 ENDPOINT_RESPONSE 1523455103585 -QUERY 66 RESPONDING 1523455103586 -QUERY 66 RESPONSE_SENT 1523455103586 -QUERY -1 REQUEST 1523455103588 -QUERY -1 SCHEDULING 1523455103588 -QUERY 67 SCHEDULED 1523455103588 -QUERY 67 ENDPOINT_REQUEST 1523455103588 -QUERY 67 ENDPOINT_RESPONSE 1523455103685 -QUERY 67 RESPONDING 1523455103685 -QUERY 67 RESPONSE_SENT 1523455103685 -UPDATE -1 REQUEST 1523455103687 -UPDATE -1 SCHEDULING 1523455103687 -UPDATE 68 SCHEDULED 1523455103687 -UPDATE 68 ENDPOINT_REQUEST 1523455103687 -UPDATE 68 ENDPOINT_RESPONSE 1523455103766 -UPDATE 68 RESPONDING 1523455103767 -UPDATE 68 RESPONSE_SENT 1523455103767 -UPDATE -1 REQUEST 1523455103770 -UPDATE -1 SCHEDULING 1523455103770 -UPDATE 69 SCHEDULED 1523455103770 -UPDATE 69 ENDPOINT_REQUEST 1523455103770 -UPDATE 69 ENDPOINT_RESPONSE 1523455103819 -UPDATE 69 RESPONDING 1523455103820 -UPDATE 69 RESPONSE_SENT 1523455103820 -UPDATE -1 REQUEST 1523455103823 -UPDATE -1 SCHEDULING 1523455103824 -UPDATE 70 SCHEDULED 1523455103824 -UPDATE 70 ENDPOINT_REQUEST 1523455103824 -UPDATE 70 ENDPOINT_RESPONSE 1523455103901 -UPDATE 70 RESPONDING 1523455103901 -UPDATE 70 RESPONSE_SENT 1523455103902 -UPDATE -1 REQUEST 1523455103904 -UPDATE -1 SCHEDULING 1523455103905 -UPDATE 71 SCHEDULED 1523455103905 -UPDATE 71 ENDPOINT_REQUEST 1523455103905 -UPDATE 71 ENDPOINT_RESPONSE 1523455103946 -UPDATE 71 RESPONDING 1523455103947 -UPDATE 71 RESPONSE_SENT 1523455103947 -UPDATE -1 REQUEST 1523455103949 -UPDATE -1 SCHEDULING 1523455103950 -UPDATE 72 ENDPOINT_REQUEST 1523455103950 -UPDATE 72 SCHEDULED 1523455103950 -UPDATE 72 ENDPOINT_RESPONSE 1523455104016 -UPDATE 72 RESPONDING 1523455104016 -UPDATE 72 RESPONSE_SENT 1523455104016 -UPDATE -1 REQUEST 1523455104018 -UPDATE -1 SCHEDULING 1523455104018 -UPDATE 73 SCHEDULED 1523455104019 -UPDATE 73 ENDPOINT_REQUEST 1523455104019 -UPDATE 73 ENDPOINT_RESPONSE 1523455104063 -UPDATE 73 RESPONDING 1523455104064 -UPDATE 73 RESPONSE_SENT 1523455104064 -UPDATE -1 REQUEST 1523455104067 -UPDATE -1 SCHEDULING 1523455104067 -UPDATE 74 ENDPOINT_REQUEST 1523455104068 -UPDATE 74 SCHEDULED 1523455104068 -UPDATE 74 ENDPOINT_RESPONSE 1523455104122 -UPDATE 74 RESPONDING 1523455104122 -UPDATE 74 RESPONSE_SENT 1523455104122 -UPDATE -1 REQUEST 1523455104125 -UPDATE -1 SCHEDULING 1523455104125 -UPDATE 75 ENDPOINT_REQUEST 1523455104126 -UPDATE 75 SCHEDULED 1523455104126 -UPDATE 75 ENDPOINT_RESPONSE 1523455104200 -UPDATE 75 RESPONDING 1523455104200 -UPDATE 75 RESPONSE_SENT 1523455104200 -UPDATE -1 REQUEST 1523455104202 -UPDATE -1 SCHEDULING 1523455104202 -UPDATE 76 SCHEDULED 1523455104202 -UPDATE 76 ENDPOINT_REQUEST 1523455104202 -UPDATE 76 ENDPOINT_RESPONSE 1523455104253 -UPDATE 76 RESPONDING 1523455104253 -UPDATE 76 RESPONSE_SENT 1523455104254 -UPDATE -1 REQUEST 1523455104255 -UPDATE -1 SCHEDULING 1523455104256 -UPDATE 77 SCHEDULED 1523455104256 -UPDATE 77 ENDPOINT_REQUEST 1523455104256 -UPDATE 77 ENDPOINT_RESPONSE 1523455104340 -UPDATE 77 RESPONDING 1523455104340 -UPDATE 77 RESPONSE_SENT 1523455104340 -QUERY -1 REQUEST 1523455104343 -QUERY -1 SCHEDULING 1523455104343 -QUERY 78 SCHEDULED 1523455104344 -QUERY 78 ENDPOINT_REQUEST 1523455104344 -QUERY 78 ENDPOINT_RESPONSE 1523455104388 -QUERY 78 RESPONDING 1523455104388 -QUERY 78 RESPONSE_SENT 1523455104388 -UPDATE -1 REQUEST 1523455104390 -UPDATE -1 SCHEDULING 1523455104391 -UPDATE 79 SCHEDULED 1523455104391 -UPDATE 79 ENDPOINT_REQUEST 1523455104391 -UPDATE 79 ENDPOINT_RESPONSE 1523455104442 -UPDATE 79 RESPONDING 1523455104442 -UPDATE 79 RESPONSE_SENT 1523455104442 -UPDATE -1 REQUEST 1523455104444 -UPDATE -1 SCHEDULING 1523455104445 -UPDATE 80 SCHEDULED 1523455104445 -UPDATE 80 ENDPOINT_REQUEST 1523455104445 -UPDATE 80 ENDPOINT_RESPONSE 1523455104507 -UPDATE 80 RESPONDING 1523455104508 -UPDATE 80 RESPONSE_SENT 1523455104508 -UPDATE -1 REQUEST 1523455104510 -UPDATE -1 SCHEDULING 1523455104510 -UPDATE 81 SCHEDULED 1523455104510 -UPDATE 81 ENDPOINT_REQUEST 1523455104510 -UPDATE 81 ENDPOINT_RESPONSE 1523455104549 -UPDATE 81 RESPONDING 1523455104549 -UPDATE 81 RESPONSE_SENT 1523455104550 -UPDATE -1 REQUEST 1523455104552 -UPDATE -1 SCHEDULING 1523455104552 -UPDATE 82 SCHEDULED 1523455104552 -UPDATE 82 ENDPOINT_REQUEST 1523455104552 -UPDATE 82 ENDPOINT_RESPONSE 1523455104638 -UPDATE 82 RESPONDING 1523455104638 -UPDATE 82 RESPONSE_SENT 1523455104639 -UPDATE -1 REQUEST 1523455104641 -UPDATE -1 SCHEDULING 1523455104642 -UPDATE 83 SCHEDULED 1523455104642 -UPDATE 83 ENDPOINT_REQUEST 1523455104642 -UPDATE 83 ENDPOINT_RESPONSE 1523455104730 -UPDATE 83 RESPONDING 1523455104731 -UPDATE 83 RESPONSE_SENT 1523455104731 -UPDATE -1 REQUEST 1523455104734 -UPDATE -1 SCHEDULING 1523455104734 -UPDATE 84 SCHEDULED 1523455104734 -UPDATE 84 ENDPOINT_REQUEST 1523455104734 -UPDATE 84 ENDPOINT_RESPONSE 1523455104833 -UPDATE 84 RESPONDING 1523455104834 -UPDATE 84 RESPONSE_SENT 1523455104834 -UPDATE -1 REQUEST 1523455104835 -UPDATE -1 SCHEDULING 1523455104836 -UPDATE 85 SCHEDULED 1523455104836 -UPDATE 85 ENDPOINT_REQUEST 1523455104836 -UPDATE 85 ENDPOINT_RESPONSE 1523455104888 -UPDATE 85 RESPONDING 1523455104889 -UPDATE 85 RESPONSE_SENT 1523455104889 -UPDATE -1 REQUEST 1523455104891 -UPDATE -1 SCHEDULING 1523455104891 -UPDATE 86 SCHEDULED 1523455104892 -UPDATE 86 ENDPOINT_REQUEST 1523455104892 -UPDATE 86 ENDPOINT_RESPONSE 1523455105018 -UPDATE 86 RESPONDING 1523455105018 -UPDATE 86 RESPONSE_SENT 1523455105019 -UPDATE -1 REQUEST 1523455105020 -UPDATE -1 SCHEDULING 1523455105021 -UPDATE 87 SCHEDULED 1523455105021 -UPDATE 87 ENDPOINT_REQUEST 1523455105021 -UPDATE 87 ENDPOINT_RESPONSE 1523455105121 -UPDATE 87 RESPONDING 1523455105122 -UPDATE 87 RESPONSE_SENT 1523455105122 -UPDATE -1 REQUEST 1523455105125 -UPDATE -1 SCHEDULING 1523455105125 -UPDATE 88 SCHEDULED 1523455105125 -UPDATE 88 ENDPOINT_REQUEST 1523455105125 -UPDATE 88 ENDPOINT_RESPONSE 1523455105230 -UPDATE 88 RESPONDING 1523455105231 -UPDATE 88 RESPONSE_SENT 1523455105231 diff --git a/engine/logs/20180412_13_45_37.csv b/engine/logs/20180412_13_45_37.csv deleted file mode 100644 index 50772280..00000000 --- a/engine/logs/20180412_13_45_37.csv +++ /dev/null @@ -1,85 +0,0 @@ -UPDATE -1 REQUEST 1523540765402 -UPDATE -1 SCHEDULING 1523540765410 -UPDATE 0 SCHEDULED 1523540765413 -UPDATE 0 ENDPOINT_REQUEST 1523540765413 -UPDATE 0 ENDPOINT_RESPONSE 1523540765599 -UPDATE 0 RESPONDING 1523540765604 -UPDATE 0 RESPONSE_SENT 1523540765609 -QUERY -1 REQUEST 1523540836549 -QUERY -1 SCHEDULING 1523540836550 -QUERY 1 SCHEDULED 1523540836550 -QUERY 1 ENDPOINT_REQUEST 1523540836550 -QUERY 1 ENDPOINT_RESPONSE 1523540836675 -QUERY 1 RESPONDING 1523540836676 -QUERY 1 RESPONSE_SENT 1523540836677 -QUERY -1 REQUEST 1523540846167 -QUERY -1 SCHEDULING 1523540846167 -QUERY 2 SCHEDULED 1523540846168 -QUERY 2 ENDPOINT_REQUEST 1523540846168 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -QUERY 2 ENDPOINT_RESPONSE 1523540846238 -ERROR 2 RESPONDING 1523540846238 -ERROR 2 RESPONSE_SENT 1523540846240 -QUERY -1 REQUEST 1523540943673 -QUERY -1 SCHEDULING 1523540943674 -QUERY 3 SCHEDULED 1523540943674 -QUERY 3 ENDPOINT_REQUEST 1523540943674 -QUERY 3 ENDPOINT_RESPONSE 1523540943732 -QUERY 3 RESPONDING 1523540943733 -QUERY 3 RESPONSE_SENT 1523540943734 -UPDATE -1 REQUEST 1523540946010 -UPDATE -1 SCHEDULING 1523540946011 -UPDATE 4 SCHEDULED 1523540946011 -UPDATE 4 ENDPOINT_REQUEST 1523540946011 -UPDATE 4 ENDPOINT_RESPONSE 1523540946067 -UPDATE 4 RESPONDING 1523540946068 -UPDATE 4 RESPONSE_SENT 1523540946068 -QUERY -1 REQUEST 1523540948280 -QUERY -1 SCHEDULING 1523540948280 -QUERY 5 SCHEDULED 1523540948281 -QUERY 5 ENDPOINT_REQUEST 1523540948281 -QUERY 5 ENDPOINT_RESPONSE 1523540948407 -QUERY 5 RESPONDING 1523540948408 -QUERY 5 RESPONSE_SENT 1523540948409 -UPDATE -1 REQUEST 1523540959911 -UPDATE -1 SCHEDULING 1523540959912 -UPDATE 6 SCHEDULED 1523540959912 -UPDATE 6 ENDPOINT_REQUEST 1523540959912 -UPDATE 6 ENDPOINT_RESPONSE 1523540959960 -UPDATE 6 RESPONDING 1523540959961 -UPDATE 6 RESPONSE_SENT 1523540959962 -QUERY -1 REQUEST 1523540961784 -QUERY -1 SCHEDULING 1523540961785 -QUERY 7 SCHEDULED 1523540961785 -QUERY 7 ENDPOINT_REQUEST 1523540961786 -QUERY 7 ENDPOINT_RESPONSE 1523540961875 -QUERY 7 RESPONDING 1523540961876 -QUERY 7 RESPONSE_SENT 1523540961877 -UPDATE -1 REQUEST 1523540996995 -UPDATE -1 SCHEDULING 1523540996995 -UPDATE 8 SCHEDULED 1523540996996 -UPDATE 8 ENDPOINT_REQUEST 1523540996996 -UPDATE 8 ENDPOINT_RESPONSE 1523540997053 -UPDATE 8 RESPONDING 1523540997054 -UPDATE 8 RESPONSE_SENT 1523540997055 -SUBSCRIBE -1 SCHEDULING 138000000 -SUBSCRIBE -1 REQUEST 135000000 -SUBSCRIBE 9 SCHEDULED 1523541001139 -SUBSCRIBE 9 ENDPOINT_REQUEST 1523541001148 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -SUBSCRIBE 9 ENDPOINT_RESPONSE 1523541001190 -Not initialized -SUBSCRIBE -1 SCHEDULING 422000000 -SUBSCRIBE -1 REQUEST 422000000 -SUBSCRIBE 10 SCHEDULED 1523541018423 -SUBSCRIBE 10 ENDPOINT_REQUEST 1523541018427 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -SUBSCRIBE 10 ENDPOINT_RESPONSE 1523541018479 -Not initialized -SUBSCRIBE -1 SCHEDULING 47000000 -SUBSCRIBE -1 REQUEST 47000000 -SUBSCRIBE 11 SCHEDULED 1523541083048 -SUBSCRIBE 11 ENDPOINT_REQUEST 1523541083051 -com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 11 path $ -SUBSCRIBE 11 ENDPOINT_RESPONSE 1523541174528 -Not initialized diff --git a/engine/logs/20180412_13_55_07.csv b/engine/logs/20180412_13_55_07.csv deleted file mode 100644 index 89b546ec..00000000 --- a/engine/logs/20180412_13_55_07.csv +++ /dev/null @@ -1,27 +0,0 @@ -*** Security check *** -Register: 43663312-cb08-473a-b521-2769cb348dc3 -Register: 43663312-cb08-473a-b521-2769cb348dc3 -Authorize identity:43663312-cb08-473a-b521-2769cb348dc3 -ID:SECRET=de7f54c8-b101-4dd1-b2fe-01a9fb198d8d:e3d45114-1cd0-4e43-9b00-50c7b48c7387 -Authorization Basic ZGU3ZjU0YzgtYjEwMS00ZGQxLWIyZmUtMDFhOWZiMTk4ZDhkOmUzZDQ1MTE0LTFjZDAtNGU0My05YjAwLTUwYzdiNDhjNzM4Nw== -Get token -Credentials: de7f54c8-b101-4dd1-b2fe-01a9fb198d8d e3d45114-1cd0-4e43-9b00-50c7b48c7387 -Access token: eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJTRVBBVGVzdCIsImF1ZCI6WyJodHRwczpcL1wvd290LmFyY2VzLnVuaWJvLml0Ojg0NDNcL3NwYXJxbCIsIndzczpcL1wvd290LmFyY2VzLnVuaWJvLml0Ojk0NDNcL3NwYXJxbCJdLCJuYmYiOjE1MjM1NDEzMDcsImlzcyI6Imh0dHBzOlwvXC93b3QuYXJjZXMudW5pYm8uaXQ6ODQ0M1wvb2F1dGhcL3Rva2VuIiwiZXhwIjoxNTIzNTQxMzEzLCJpYXQiOjE1MjM1NDEzMDgsImp0aSI6ImRlN2Y1NGM4LWIxMDEtNGRkMS1iMmZlLTAxYTlmYjE5OGQ4ZDplM2Q0NTExNC0xY2QwLTRlNDMtOWIwMC01MGM3YjQ4YzczODcifQ.j--osVCHej_Nih-CWtbBxcZgLbkKeoYyoJzZo3mkwQSY7sZIJ9Um_9qKRubhuUm4CBYDhKk3IQBXt7nf_nlbmxPHTSdrxAfmaIX47pOHh7wDk-jjA_DFnzBFAStlemj7RI9nZA1IN26S7IiuJ9KVGAQoa7KImDDj5996-K2VVmzOReSOW5vGY2_HJy0YBE-jDWYNqquyQrvTjPCw4jJLFpff6s0aVMKtMtbg12WYntwF8DixpG6FmuYYB7jF_Nu5rcG78Ehw4DyFu4nD5wWvfb-aOsxk2saHSqDGqI-TTEkpXEN7nDyKbxzd5psVovbYSgHO-61FIjbf0gCzEwpPhw -Validate token -PASSED -********************** -@onOpen WebSocket: Resource descriptor: /subscribe -Message from: /127.0.0.1:50948 [{"subscribe":{"sparql":"PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName }"}}] -SUBSCRIBE -1 SCHEDULING 989000000 -SUBSCRIBE -1 REQUEST 987000000 -Get token #0 (Available: 99) -Subscribe request #0 -SUBSCRIBE 0 SCHEDULED 1523541316991 -SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SPU: sepa://spuid/ab9d1583-1505-4d0b-b898-59e9eb133d49 request: SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SPU init -Process SPARQL query SUBSCRIBE #0 PREFIX td: PREFIX rdf: PREFIX qmul: PREFIX wot: SELECT ?thing ?property ?action ?event ?tName ?pName ?aName ?eName WHERE {?thing rdf:type td:Thing . ?thing td:hasName ?tName . OPTIONAL {?thing td:hasEvent ?event . ?event td:hasName ?eName } . OPTIONAL { ?thing td:hasAction ?action . ?action td:hasName ?aName } . OPTIONAL { ?thing td:hasProperty ?property . ?property td:hasName ?pName } -SUBSCRIBE 0 ENDPOINT_REQUEST 1523541317001 -Execute SPARQL 1.1 QUERY (timeout: 5000 ms) GET http://mml.arces.unibo.it:8890/sparql?query=PREFIX+td%3A%3Chttp%3A%2F%2Fwot.arces.unibo.it%2Fontology%2Fweb_of_things%23%3E+PREFIX+rdf%3A%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23%3E+PREFIX+qmul%3A%3Chttp%3A%2F%2Feecs.qmul.ac.uk%2Fwot%23%3E+PREFIX+wot%3A%3Chttp%3A%2F%2Fwot.arces.unibo.it%2Fsepa%23%3E+SELECT+%3Fthing+%3Fproperty+%3Faction+%3Fevent+%3FtName+%3FpName+%3FaName+%3FeName+WHERE+%7B%3Fthing+rdf%3Atype+td%3AThing+.+%3Fthing+td%3AhasName+%3FtName+.+OPTIONAL+%7B%3Fthing+td%3AhasEvent+%3Fevent+.+%3Fevent+td%3AhasName+%3FeName+%7D+.+OPTIONAL+%7B+%3Fthing+td%3AhasAction+%3Faction+.+%3Faction+td%3AhasName+%3FaName+%7D+.+OPTIONAL+%7B+%3Fthing+td%3AhasProperty+%3Fproperty+.+%3Fproperty+td%3AhasName+%3FpName+%7D&format=application%2Fsparql-results%2Bjson&default-graph-uri=http%3A%2F%2Fdefault HTTP/1.1 -QUERY_TIME (112 ms) diff --git a/engine/logs/20180412_13_59_52.csv b/engine/logs/20180412_13_59_52.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_00_13.csv b/engine/logs/20180412_14_00_13.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_01_11.csv b/engine/logs/20180412_14_01_11.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_01_21.csv b/engine/logs/20180412_14_01_21.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180412_14_03_44.csv b/engine/logs/20180412_14_03_44.csv deleted file mode 100644 index e69de29b..00000000 diff --git a/engine/logs/20180419_09_42_39.csv b/engine/logs/20180419_09_42_39.csv deleted file mode 100644 index e69de29b..00000000 From 3884aa8ed5b66e5e4a10ce4839f0e70b8eb43a39 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 5 Jul 2018 16:08:56 +0200 Subject: [PATCH 30/76] Fix itegration test to use latest api --- .../test/java/engine/ITUpdateProcessor.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/engine/src/test/java/engine/ITUpdateProcessor.java b/engine/src/test/java/engine/ITUpdateProcessor.java index 5fde0610..e109aa7c 100644 --- a/engine/src/test/java/engine/ITUpdateProcessor.java +++ b/engine/src/test/java/engine/ITUpdateProcessor.java @@ -25,7 +25,8 @@ public void init() throws SEPAPropertiesException, SEPAProtocolException { @Test public void testInsertAddRemoved(){ UpdateRequest updateRequest = new UpdateRequest("INSERT{ }Where{}"); - Response process = updateProcessor.process(updateRequest, 5000); + updateRequest.setTimeout(5000); + Response process = updateProcessor.process(updateRequest); assertTrue("Not a UpdateResponse",process instanceof UpdateResponseWithAR); UpdateResponseWithAR uAR = (UpdateResponseWithAR) process; @@ -33,9 +34,9 @@ public void testInsertAddRemoved(){ assertTrue("Added is empty",!uAR.getAdded().isEmpty()); assertTrue("Removed is not empty",uAR.getRemoved().isEmpty()); - String sub = uAR.getAdded().getBindings().get(0).getBindingValue("subject"); - String pred = uAR.getAdded().getBindings().get(0).getBindingValue("predicate"); - String obj = uAR.getAdded().getBindings().get(0).getBindingValue("object"); + String sub = uAR.getAdded().getBindings().get(0).getValue("subject"); + String pred = uAR.getAdded().getBindings().get(0).getValue("predicate"); + String obj = uAR.getAdded().getBindings().get(0).getValue("object"); assertEquals("test://it/update",sub); assertEquals("test://it/update/pre",pred); @@ -45,7 +46,8 @@ public void testInsertAddRemoved(){ @Test public void testDeleteAddRemoved(){ UpdateRequest updateRequest = new UpdateRequest("DELETE{ }Where{}"); - Response process = updateProcessor.process(updateRequest, 5000); + Response process = updateProcessor.process(updateRequest); + updateRequest.setTimeout(5000); assertTrue("Not a UpdateResponse",process instanceof UpdateResponseWithAR); UpdateResponseWithAR uAR = (UpdateResponseWithAR) process; @@ -53,9 +55,9 @@ public void testDeleteAddRemoved(){ assertTrue("Removed is empty",!uAR.getRemoved().isEmpty()); assertTrue("Added is not empty",uAR.getAdded().isEmpty()); - String sub = uAR.getRemoved().getBindings().get(0).getBindingValue("subject"); - String pred = uAR.getRemoved().getBindings().get(0).getBindingValue("predicate"); - String obj = uAR.getRemoved().getBindings().get(0).getBindingValue("object"); + String sub = uAR.getRemoved().getBindings().get(0).getValue("subject"); + String pred = uAR.getRemoved().getBindings().get(0).getValue("predicate"); + String obj = uAR.getRemoved().getBindings().get(0).getValue("object"); assertEquals("test://it/update",sub); assertEquals("test://it/update/pre",pred); From 5f68afbcda528eeda4b8df9396791793683f1f61 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 5 Jul 2018 17:48:16 +0200 Subject: [PATCH 31/76] Tested with "equals subscriptions" optimization Need to be deeper tested before publish as new release (0.9.5). It has to be merged with the new update processing feature (who detects inserted and removed quads) --- .../sepa/commons/request/QueryRequest.java | 15 ++ .../wot/sepa/commons/request/Request.java | 10 - .../commons/request/SubscribeRequest.java | 15 ++ .../sepa/commons/request/UpdateRequest.java | 15 ++ .../sepa/commons/response/Notification.java | 26 +++ .../arces/wot/sepa/engine/core/Engine.java | 4 +- .../wot/sepa/engine/processing/Processor.java | 5 +- .../SubscribeProcessor.java | 178 ++++++++++++------ .../SubscribeProcessorMBean.java | 2 +- .../processing/UpdateProcessingQueue.java | 24 --- .../engine/processing/subscriptions/ISPU.java | 2 +- .../engine/processing/subscriptions/SPU.java | 67 +++---- .../subscriptions/SPUEndOfProcessing.java | 2 +- .../processing/subscriptions/SPUListener.java | 7 - .../processing/subscriptions/SPUManager.java | 93 +++++++-- .../processing/subscriptions/SPUNaive.java | 11 +- .../SPUProcessingResultsQueue.java | 26 --- .../processing/subscriptions/SPUSync.java | 55 ------ .../processing/subscriptions/Subscriber.java | 25 +-- .../subscriptions/Unsubscriber.java | 5 +- .../subscription/SPUMangerTest.java | 8 +- tools/arces-demo.jsap | 7 + 22 files changed, 341 insertions(+), 261 deletions(-) rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/{subscriptions => }/SubscribeProcessor.java (55%) rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/{subscriptions => }/SubscribeProcessorMBean.java (88%) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingQueue.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUListener.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUProcessingResultsQueue.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index 90b721fc..3da6c929 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -108,4 +108,19 @@ public String getNamedGraphUri() { return null; } } + + /** + * Default implementation. Two requests are equal if they belong to the same class and their SPARQL strings are equals. SPARQL matching should be based on SPARQL algebra + * and SPARQL semantics. The default implementation provides a syntax based matching. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof QueryRequest)) return false; + return sparql.equals(((QueryRequest)obj).sparql); + } + + @Override + public int hashCode() { + return sparql.hashCode(); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java index 17e940c8..73fb0c5d 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java @@ -137,16 +137,6 @@ public boolean isUnsubscribeRequest() { return this.getClass().equals(UnsubscribeRequest.class); } - /** - * Default implementation. Two requests are equal if they belong to the same class and their SPARQL strings are equals. SPARQL matching should be based on SPARQL algebra - * and SPARQL semantics. The default implementation provides a syntax based matching. - */ - @Override - public boolean equals(Object obj) { - if (!obj.getClass().equals(this.getClass())) return false; - return sparql.equals(((QueryRequest)obj).sparql); - } - public HTTPMethod getHttpMethod() { return method; } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java index a9942a11..99277150 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java @@ -78,4 +78,19 @@ public String toString() { public String getAlias() { return alias; } + + /** + * Default implementation. Two requests are equal if they belong to the same class and their SPARQL strings are equals. SPARQL matching should be based on SPARQL algebra + * and SPARQL semantics. The default implementation provides a syntax based matching. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SubscribeRequest)) return false; + return sparql.equals(((SubscribeRequest)obj).sparql); + } + + @Override + public int hashCode() { + return sparql.hashCode(); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java index c0bac47d..2081b036 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java @@ -124,4 +124,19 @@ public String getUsingNamedGraphUri() { public String getAcceptHeader() { return "application/json"; } + + /** + * Default implementation. Two requests are equal if they belong to the same class and their SPARQL strings are equals. SPARQL matching should be based on SPARQL algebra + * and SPARQL semantics. The default implementation provides a syntax based matching. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof UpdateRequest)) return false; + return sparql.equals(((UpdateRequest)obj).sparql); + } + + @Override + public int hashCode() { + return sparql.hashCode(); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java index 0521abd8..d3360a9e 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java @@ -63,6 +63,32 @@ public Notification(String spuid, ARBindingsResults results, Integer sequence) { json.add("notification", response); } + /** + * Instantiates a new notification. + * + * @param spuid + * the spuid + * @param results + * the results + */ + public Notification(String spuid, ARBindingsResults results) { + super(); + + JsonObject response = new JsonObject(); + + if (spuid != null) + response.add("spuid", new JsonPrimitive(spuid)); + + response.add("sequence", new JsonPrimitive(0)); + + if (results != null) { + response.add("addedResults", results.getAddedBindings().toJson()); + response.add("removedResults", results.getRemovedBindings().toJson()); + } + + json.add("notification", response); + } + /** * Instantiates a new notification. * diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index c1524c24..705762f1 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -59,12 +59,12 @@ * Event Processing Architecture (SEPA) * * @author Luca Roffia (luca.roffia@unibo.it) - * @version 0.9.2 + * @version 0.9.5 */ public class Engine implements EngineMBean { private static Engine engine; - private static String version = "0.9.2"; + private static String version = "0.9.5"; private EngineProperties properties = null; // Scheduler request queue diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index 02367190..ebf6d852 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -21,7 +21,6 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SubscribeProcessor; import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; @@ -97,13 +96,15 @@ public void run() { } catch (InterruptedException e) { return; } + // Process update request request.setTimeout(ProcessorBeans.getUpdateTimeout()); Response ret = updateProcessor.process(request); - // // Notify update result + // Notify update result queue.addResponse(ret); + // Subscription processing if (ret.isUpdateResponse()) { subscribeProcessor.process((UpdateResponse) ret); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java similarity index 55% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java index 4de9dae8..c7f199d4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java @@ -16,9 +16,12 @@ along with this program. If not, see . */ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; +package it.unibo.arces.wot.sepa.engine.processing; +import java.io.IOException; import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; import java.util.concurrent.Semaphore; import org.apache.logging.log4j.Logger; @@ -30,7 +33,9 @@ import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; @@ -39,110 +44,167 @@ import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.EventHandler; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUNaive; -public class SubscribeProcessor implements SubscribeProcessorMBean { +class SubscribeProcessor implements SubscribeProcessorMBean, EventHandler { private final Logger logger = LogManager.getLogger(); - private final Subscriber subscriber; - private final Unsubscriber unsubscriber; - private SPARQL11Properties endpointProperties; private Semaphore endpointSemaphore; - - private SPUManager spuManager = new SPUManager(); - + + private SPUManager spuManager = new SPUManager(); + + // REAL spuid => list of FAKE spuids + private HashMap> spuidPoll = new HashMap>(); + + // FAKE spuid ==> handler + private HashMap handlers = new HashMap(); + + // Handler ==> sequence number + private HashMap sequenceNumbers = new HashMap(); + + // FAKE spuid ==> REAL spuid + private HashMap fakeMap = new HashMap(); + public SubscribeProcessor(SPARQL11Properties endpointProperties, EngineProperties engineProperties, - Semaphore endpointSemaphore) { + Semaphore endpointSemaphore) { this.endpointProperties = endpointProperties; this.endpointSemaphore = endpointSemaphore; SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SubscribeProcessorBeans.setSPUProcessingTimeout(engineProperties.getSPUProcessingTimeout()); - - this.subscriber = new Subscriber(spuManager); - this.unsubscriber = new Unsubscriber(spuManager); } public void start() { - this.subscriber.start(); - this.unsubscriber.start(); + spuManager.start(); } public void stop() { - this.subscriber.finish(); - this.unsubscriber.finish(); + spuManager.stop(); + } - this.subscriber.interrupt(); - this.unsubscriber.interrupt(); + @Override + public void sendResponse(Response response) throws IOException { + logger.warn("Not implemented: " + response); } - public Response subscribe(SubscribeRequest req, EventHandler handler) { - logger.trace(req.toString()); + @Override + public void notifyEvent(Notification notify) throws IOException { + for (String spuid : spuidPoll.get(notify.getSpuid())) { + EventHandler handler = handlers.get(spuid); - SubscribeProcessorBeans.subscribeRequest(); + handler.notifyEvent(new Notification(spuid, notify.getARBindingsResults(), sequenceNumbers.get(handler))); - // TODO: choose different kinds of SPU based on subscribe request - SPU spu = null; - try { - spu = new SPUNaive(req, handler, endpointProperties, endpointSemaphore, spuManager); - } catch (SEPAProtocolException e) { - logger.debug("SPU creation failed: " + e.getMessage()); - - return new ErrorResponse(req.getToken(), 500, "SPU creation failed: " + req.toString()); + sequenceNumbers.put(handler, sequenceNumbers.get(handler) + 1); } + } - logger.debug("SPU init"); + public void process(UpdateResponse update) { + logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); + Instant start = Instant.now(); - Response init = spu.init(); + // Start subscription processing + spuManager.startProcessing(update); - if (init.isError()) { - logger.debug("SPU initialization failed"); - } else { - logger.debug("Add SPU to activation queue"); - try { - subscriber.activate(spu); - } catch (InterruptedException e) { - return new ErrorResponse(req.getToken(),500,"Failed activating SPU"); - } - } + // Wait all SPUs completing processing (or timeout) + spuManager.waitEndOfProcessing(); + + Instant stop = Instant.now(); + + SubscribeProcessorBeans.timings(start, stop); - return init; + logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); } + + public Response subscribe(SubscribeRequest req, EventHandler handler) { + logger.trace(req.toString()); + + SubscribeProcessorBeans.subscribeRequest(); + // Is SPU already available or do we need to create a new one? + ISPU spu = spuManager.getSPU(req); + if (spu == null) spu = createSPU(req); + if (spu == null) return new ErrorResponse(req.getToken(), 500, "SPU creation failed: " + req.toString()); + + // Generate a fake SPU id + String spuid = spuManager.generateSpuid(); + + // Register handler + registerHandler(spu.getUUID(), spuid, handler); + + return new SubscribeResponse(req.getToken(), spuid, spu.getLastBindings()); + } + public Response unsubscribe(UnsubscribeRequest req) { logger.trace(req); SubscribeProcessorBeans.unsubscribeRequest(); String spuid = req.getSubscribeUUID(); + String masterSpuid = fakeMap.get(spuid); - if (!spuManager.isValidSpuId(spuid)) - return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + spuid); + logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); + + if (masterSpuid == null) return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + spuid); + if (!spuManager.isValidSpuId(masterSpuid)) return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + masterSpuid); - try { - unsubscriber.deactivate(spuid); - } catch (InterruptedException e) { - return new ErrorResponse(req.getToken(), 500, "Failed to unsubscribe: " + spuid); + // Unregister handler + unregisterHandler(masterSpuid, spuid); + + if (spuidPoll.get(masterSpuid).isEmpty()) { + spuidPoll.remove(masterSpuid); + + // Deactivate SPU + spuManager.deactivate(masterSpuid); } return new UnsubscribeResponse(req.getToken(), spuid); } + + // TODO: choose different kinds of SPU based on subscribe request + private ISPU createSPU(SubscribeRequest req) { + ISPU spu; - public void process(UpdateResponse update) { - logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); - Instant start = Instant.now(); + try { + spu = new SPUNaive(req, this, endpointProperties, endpointSemaphore, spuManager); + } catch (SEPAProtocolException e) { + logger.debug("SPU creation failed: " + e.getMessage()); + return null; + } - logger.debug("Activate SPUs (Total: " + spuManager.size() + ")"); + // Initialize SPU + Response init = spu.init(); + if (init.isError()) { + logger.error("SPU initialization failed"); + return null; + } - spuManager.startProcessing(update); + logger.debug("Add SPU to activation queue"); - // Wait all SPUs completing processing (or timeout) - spuManager.waitEndOfProcessing(); + // Request SPU activation + if(!spuManager.activate(spu, req)) return null; - Instant stop = Instant.now(); - SubscribeProcessorBeans.timings(start, stop); + return spu; + } - logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); + private void registerHandler(String masterSpuid, String spuid, EventHandler handler) { + logger.debug("Register SPU handler: " + spuid); + if (spuidPoll.get(masterSpuid) == null) + spuidPoll.put(masterSpuid, new ArrayList()); + + spuidPoll.get(masterSpuid).add(spuid); + handlers.put(spuid, handler); + sequenceNumbers.put(handler, 1); + fakeMap.put(spuid, masterSpuid); + } + + private void unregisterHandler(String masterSpuid, String spuid) { + fakeMap.remove(spuid); + sequenceNumbers.remove(handlers.get(spuid)); + handlers.remove(spuid); + spuidPoll.get(masterSpuid).remove(spuid); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java similarity index 88% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessorMBean.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java index 91a5c853..1ef5f56f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SubscribeProcessorMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java @@ -1,4 +1,4 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; +package it.unibo.arces.wot.sepa.engine.processing; public interface SubscribeProcessorMBean { public long getRequests(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingQueue.java deleted file mode 100644 index 6b37d98d..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingQueue.java +++ /dev/null @@ -1,24 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUEndOfProcessing; - -import java.util.concurrent.ConcurrentLinkedQueue; - -public class UpdateProcessingQueue { - private ConcurrentLinkedQueue responses = new ConcurrentLinkedQueue(); - - public void waitUpdateEOP() throws InterruptedException { - while(responses.poll()==null) { - synchronized(responses) { - responses.wait(); - } - } - } - - public void updateEOP(SPUEndOfProcessing eop) { - responses.add(eop); - synchronized(responses) { - responses.notify(); - } - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java index edd6ec97..d6831a4d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java @@ -8,7 +8,7 @@ public interface ISPU extends Runnable { Response init(); - BindingsResults getCurrentResults(); + BindingsResults getLastBindings(); void terminate(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index 22d9fa46..5a94973a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -19,7 +19,6 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; import java.io.IOException; -import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Semaphore; @@ -48,13 +47,12 @@ * @version 0.1 */ -//public abstract class SPU extends Observable implements Runnable { -public abstract class SPU implements ISPU { +// public abstract class SPU extends Observable implements Runnable { +abstract class SPU implements ISPU { private final Logger logger; // The URI of the subscription (i.e., sepa://spuid/UUID) private String uuid = null; - private String prefix = "sepa://spuid/"; // Update queue protected ConcurrentLinkedQueue updateQueue = new ConcurrentLinkedQueue(); @@ -62,30 +60,32 @@ public abstract class SPU implements ISPU { protected QueryProcessor queryProcessor; protected SubscribeRequest request; + + // Handler of notifications protected EventHandler handler; // Thread loop private boolean running = true; - // Query first results - protected BindingsResults firstResults = null; + // Last bindings results + protected BindingsResults lastBindings = null; - //Notification result + // Notification result private Response notify; // List of processing SPU - private SPUManager sync; + private SPUManager manager; public SPU(SubscribeRequest subscribe, SPARQL11Properties properties, EventHandler eventHandler, - Semaphore endpointSemaphore, SPUManager sync) throws SEPAProtocolException { + Semaphore endpointSemaphore, SPUManager manager) throws SEPAProtocolException { if (eventHandler == null) throw new SEPAProtocolException(new IllegalArgumentException("Subscribe event handler is null")); - if (sync == null) - throw new SEPAProtocolException(new IllegalArgumentException("SPU sync is null")); + if (manager == null) + throw new SEPAProtocolException(new IllegalArgumentException("SPU manager is null")); - this.sync = sync; + this.manager = manager; - uuid = prefix + UUID.randomUUID().toString(); + uuid = manager.generateSpuid(); logger = LogManager.getLogger("SPU" + uuid); request = subscribe; @@ -96,11 +96,15 @@ public SPU(SubscribeRequest subscribe, SPARQL11Properties properties, EventHandl running = true; } - public abstract Response processInternal(UpdateResponse update,int timeout); - + public SubscribeRequest getSubscribe() { + return request; + } + + public abstract Response processInternal(UpdateResponse update, int timeout); + @Override - public BindingsResults getCurrentResults() { - return firstResults; + public BindingsResults getLastBindings() { + return lastBindings; } @Override @@ -131,7 +135,6 @@ public void process(UpdateResponse res) { } } - @Override public void run() { while (running) { @@ -141,24 +144,22 @@ public void run() { // Processing update logger.debug("* PROCESSING *"); - //Asynchronous processing and waiting for result - notify = processInternal(updateResponse,SubscribeProcessorBeans.getSPUProcessingTimeout()); - + // Asynchronous processing and waiting for result + notify = processInternal(updateResponse, SubscribeProcessorBeans.getSPUProcessingTimeout()); + // Notify event handler - if (handler != null) { - if (notify.isNotification()) - try { - handler.notifyEvent((Notification)notify); - } catch (IOException e) { - logger.error("Failed to notify "+notify); - } - else logger.debug("Not a notification: "+notify); - } - else logger.error("Handler is null"); - + if (notify.isNotification()) + try { + handler.notifyEvent((Notification) notify); + } catch (IOException e) { + logger.error("Failed to notify " + notify); + } + else + logger.debug("Not a notification: " + notify); + // Notify SPU manager logger.debug("Notify SPU manager. Running: " + running); - sync.endProcessing(this); + manager.endProcessing(this); } // Wait next request... diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUEndOfProcessing.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUEndOfProcessing.java index c9a65fde..00f7de9b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUEndOfProcessing.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUEndOfProcessing.java @@ -2,7 +2,7 @@ import it.unibo.arces.wot.sepa.commons.response.Response; -public class SPUEndOfProcessing extends Response { +class SPUEndOfProcessing extends Response { private boolean timeout; public SPUEndOfProcessing(boolean b) { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUListener.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUListener.java deleted file mode 100644 index 731d2ab4..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; - -import it.unibo.arces.wot.sepa.commons.response.Notification; - -public interface SPUListener { - public void SPUNotify(Notification notify); -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index f6b3779d..0b4f21c6 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -1,5 +1,6 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; @@ -7,6 +8,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.UUID; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -18,11 +20,42 @@ public class SPUManager { private final Logger logger = LogManager.getLogger(); - // Registered SPUs - private HashMap spus = new HashMap<>(); + // SPUID ==> SPU + private final HashMap spus = new HashMap<>(); + // Request ==> SPU + private final HashMap request2Spu = new HashMap<>(); + + // SPUID ==> Request + private final HashMap spuid2Request = new HashMap<>(); + // SPUs processing set - private HashSet processingSpus = new HashSet<>(); + private final HashSet processingSpus = new HashSet<>(); + + private final Subscriber subscriber; + private final Unsubscriber unsubscriber; + + public SPUManager() { + this.subscriber = new Subscriber(this); + this.unsubscriber = new Unsubscriber(this); + } + + public void start() { + this.subscriber.start(); + this.unsubscriber.start(); + } + + public void stop() { + this.subscriber.finish(); + this.unsubscriber.finish(); + + this.subscriber.interrupt(); + this.unsubscriber.interrupt(); + } + + public String generateSpuid() { + return "sepa://spuid/" + UUID.randomUUID().toString(); + } public void startProcessing(UpdateResponse update) { synchronized (processingSpus) { @@ -58,10 +91,6 @@ public void waitEndOfProcessing() { } } - public boolean isEmpty() { - return processingSpus.isEmpty(); - } - public void endProcessing(SPU s) { synchronized (processingSpus) { processingSpus.remove(s); @@ -70,20 +99,28 @@ public void endProcessing(SPU s) { } } - public synchronized void register(ISPU spu) { - synchronized (processingSpus) { - spus.put(spu.getUUID(), spu); - } + public boolean isEmpty() { + return processingSpus.isEmpty(); + } + + public synchronized void register(ISPU spu, SubscribeRequest request) { + logger.debug("Register SPU: "+spu.getUUID()); + + spus.put(spu.getUUID(), spu); + request2Spu.put(request, spu); + spuid2Request.put(spu.getUUID(), request); } public synchronized void unRegister(String spuID) { - synchronized (processingSpus) { - if (!isValidSpuId(spuID)) { - throw new IllegalArgumentException("Unregistering a not existing SPUID: " + spuID); - } - spus.get(spuID).terminate(); - spus.remove(spuID); + if (!isValidSpuId(spuID)) { + throw new IllegalArgumentException("Unregistering a not existing SPUID: " + spuID); } + + spus.get(spuID).terminate(); + + spus.remove(spuID); + request2Spu.remove(spuid2Request.get(spuID)); + spuid2Request.remove(spuID); } public synchronized boolean isValidSpuId(String id) { @@ -102,4 +139,26 @@ public synchronized int size() { return spus.values().size(); } + public synchronized ISPU getSPU(SubscribeRequest req) { + return request2Spu.get(req); + } + + public boolean activate(ISPU spu, SubscribeRequest req) { + try { + subscriber.activate(spu, req); + } catch (InterruptedException e) { + return false; + } + return true; + } + + public boolean deactivate(String spuid) { + try { + unsubscriber.deactivate(spuid); + } catch (InterruptedException e) { + return false; + } + return true; + } + } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index 83b0040b..13577542 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -41,9 +41,6 @@ public class SPUNaive extends SPU { private final Logger logger; - private BindingsResults lastBindings = null; - private Integer sequence = 1; - public SPUNaive(SubscribeRequest subscribe, EventHandler handler, SPARQL11Properties endpointProperties, Semaphore endpointSemaphore, SPUManager sync) throws SEPAProtocolException { super(subscribe, endpointProperties, handler, endpointSemaphore, sync); @@ -65,11 +62,10 @@ public Response init() { } lastBindings = ((QueryResponse) ret).getBindingsResults(); - firstResults = new BindingsResults(lastBindings); - logger.debug("First results: " + firstResults.toString()); + logger.debug("First results: " + lastBindings.toString()); - return new SubscribeResponse(request.getToken(), getUUID(), request.getAlias(), getCurrentResults()); + return new SubscribeResponse(request.getToken(), getUUID(), request.getAlias(), lastBindings); } @Override @@ -126,7 +122,8 @@ public Response processInternal(UpdateResponse update,int timeout) { lastBindings = currentBindings; // Send notification (or end processing indication) - if (!added.isEmpty() || !removed.isEmpty()) ret = new Notification(getUUID(), new ARBindingsResults(added, removed), sequence++); + if (!added.isEmpty() || !removed.isEmpty()) + ret = new Notification(getUUID(), new ARBindingsResults(added, removed)); } catch (Exception e) { ret = new ErrorResponse(500, e.getMessage()); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUProcessingResultsQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUProcessingResultsQueue.java deleted file mode 100644 index 65a888b2..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUProcessingResultsQueue.java +++ /dev/null @@ -1,26 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; - -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPU; - -import java.util.concurrent.ConcurrentLinkedQueue; - -public class SPUProcessingResultsQueue { - private ConcurrentLinkedQueue responses = new ConcurrentLinkedQueue(); - - public SPU waitSPUEndOfProcessing() throws InterruptedException { - SPU spu; - while((spu=responses.poll())==null) { - synchronized(responses) { - responses.wait(); - } - } - return spu; - } - - public void endOfProcessing(SPU spu) { - responses.add(spu); - synchronized(responses) { - responses.notify(); - } - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java deleted file mode 100644 index 872d14be..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUSync.java +++ /dev/null @@ -1,55 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; - -import java.util.Collection; -import java.util.HashSet; - -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPU; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; - -public class SPUSync { - private final Logger logger = LogManager.getLogger(); - - // SPU synchronization - private HashSet processingSpus = new HashSet<>(); - - public void startProcessing(Collection spus) { - synchronized (processingSpus) { - processingSpus.clear(); - processingSpus.addAll(spus); - } - } - - public void waitEndOfProcessing() { - // Wait all SPUs completing processing (or timeout) - synchronized (processingSpus) { - while (!processingSpus.isEmpty()) { - logger.debug(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); - try { - processingSpus.wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); - } catch (InterruptedException e) { - return; - } - } - // TIMEOUT - if (!processingSpus.isEmpty()) { - logger.error("Timeout on SPU processing. SPUs still running: " + processingSpus.size()); - } - } - } - - public boolean isEmpty() { - return processingSpus.isEmpty(); - } - - public void endProcessing(SPU s) { - synchronized (processingSpus) { - processingSpus.remove(s); - logger.debug("SPUs left: " + processingSpus.size()); - processingSpus.notify(); - } - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java index 070d4211..3db20808 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -1,43 +1,45 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -//import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; -public class Subscriber extends Thread { +class Subscriber extends Thread { private final Logger logger = LogManager.getLogger(); private final AtomicBoolean end = new AtomicBoolean(false); + private final BlockingQueue subscriptionQueue = new LinkedBlockingQueue(); + private final BlockingQueue requestQueue = new LinkedBlockingQueue(); + private final SPUManager spuManager; - //private LinkedBlockingQueue subscribeQueue = new LinkedBlockingQueue<>(); - - //public Subscriber(BlockingQueue subscriptionQueue, SPUManager manager){ public Subscriber(SPUManager manager){ super("SEPA-SPU-Subscriber"); - //this.subscriptionQueue = subscriptionQueue; spuManager = manager; } @Override public void run() { while (!end.get()) { - ISPU spu = null; try { // Wait for a new SPU to be activated - spu = subscriptionQueue.take(); + ISPU spu = subscriptionQueue.take(); + SubscribeRequest request = requestQueue.take(); // Start the SPU thread + logger.debug("Starting SPU: "+spu.getUUID()); Thread th = new Thread(spu); th.setName("SPU_" + spu.getUUID()); th.start(); - - spuManager.register(spu); + logger.debug("Started SPU: "+spu.getUUID()); + + spuManager.register(spu,request); SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); logger.debug(spu.getUUID() + " ACTIVATED (total: " + spuManager.size() + ")"); @@ -47,8 +49,9 @@ public void run() { } } - public void activate(ISPU spu) throws InterruptedException { + public void activate(ISPU spu,SubscribeRequest request) throws InterruptedException { subscriptionQueue.put(spu); + requestQueue.put(request); } public void finish(){ diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java index 2afa10ad..f3c54307 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java @@ -1,6 +1,7 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,7 +15,7 @@ * * @see SPUManager */ -public class Unsubscriber extends Thread { +class Unsubscriber extends Thread { private final Logger logger = LogManager.getLogger(); private final BlockingQueue unsubscribeQueue = new LinkedBlockingQueue(); private final SPUManager spuManager; @@ -22,7 +23,6 @@ public class Unsubscriber extends Thread { public Unsubscriber(SPUManager manager){ super("SEPA-SPU-Unsubscriber"); - //this.unsubscribeQueue = unsubscribeQueue; spuManager = manager; } @@ -45,6 +45,7 @@ public void run() { } public void deactivate(String spuid) throws InterruptedException { + logger.debug("Deactivate: "+spuid); unsubscribeQueue.put(spuid); } diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java index c6cccabe..54a89e6b 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java @@ -21,14 +21,14 @@ public void Init(){ @Test public void registerTest(){ FakeSPU spu = new FakeSPU("Test"); - spuManger.register(spu); + spuManger.register(spu,null); Assert.assertEquals("SPU not registered",spuManger.size(),1); } @Test public void unRegisterTest(){ FakeSPU spu = new FakeSPU("123Test"); - spuManger.register(spu); + spuManger.register(spu,null); Assert.assertEquals("SPU not registered",spuManger.size(),1); spuManger.unRegister("123Test"); Assert.assertEquals("SPU not succesfully deleted",spuManger.size(),0); @@ -42,7 +42,7 @@ public void unRegisterInvalidID(){ @Test public void isValidUIDTest(){ Assert.assertFalse(spuManger.isValidSpuId("Cap")); - spuManger.register(new FakeSPU("Ironman")); + spuManger.register(new FakeSPU("Ironman"),null); Assert.assertTrue(spuManger.isValidSpuId("Ironman")); } @@ -60,7 +60,7 @@ public Response init() { } @Override - public BindingsResults getCurrentResults() { + public BindingsResults getLastBindings() { return null; } diff --git a/tools/arces-demo.jsap b/tools/arces-demo.jsap index b743ff43..deb67d4a 100644 --- a/tools/arces-demo.jsap +++ b/tools/arces-demo.jsap @@ -661,6 +661,13 @@ } }, "queries": { + "LOG_TRIPLES_NUMBER" :{ + "sparql" : "SELECT (COUNT(?x) AS ?n) WHERE {?x ?y ?z}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log", + "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log" + } + }, "LOG_QUANTITY": { "sparql": "SELECT * WHERE {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value; time:inXSDDateTimeStamp ?timestamp}", "graphs": { From 80c019e0dd0f536e6418b334b508267e4b188e0f Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 6 Jul 2018 12:14:34 +0200 Subject: [PATCH 32/76] Security JUnit test Implemented JUnit test for chat Next step: to implement the secure chat test and test more functionalities (e.g., number of notifications) --- client-api/sepa.jks | Bin 0 -> 2317 bytes .../wot/sepa/api/SPARQL11SEProtocol.java | 6 +- .../websocket/SEPAWebsocketClient.java | 2 +- .../wot/sepa/commons/request/Request.java | 4 + .../sepa/commons/response/JWTResponse.java | 1 - .../arces/wot/sepa/commons/response/Ping.java | 51 ----- .../wot/sepa/commons/response/Response.java | 3 - .../security/AuthenticationProperties.java | 23 ++- .../commons/security/SEPASecurityManager.java | 180 ++++++++++++----- .../wot/sepa/api/ConfigurationProvider.java | 2 +- .../arces/wot/sepa/api/ITProtocolTest.java | 71 ++++--- .../wot/sepa/api/ITSecureProtocolTest.java | 103 +++++----- .../unibo/arces/wot/sepa/api/TestQueries.java | 22 -- .../src/test/resources/sepatest-secure.jsap | 75 +------ client-api/src/test/resources/sepatest.jsap | 58 +----- .../dependability/AuthorizationManager.java | 14 +- tools/pom.xml | 23 ++- .../sepa/apps/chat/ConfigurationProvider.java | 22 ++ .../wot/sepa/apps/chat/SEPAChatTest.java | 102 +++++----- tools/src/test/resources/chat.jsap | 189 ++++++++++++++++++ 20 files changed, 543 insertions(+), 408 deletions(-) create mode 100644 client-api/sepa.jks delete mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Ping.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/TestQueries.java create mode 100644 tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java rename tools/src/{main => test}/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java (58%) create mode 100644 tools/src/test/resources/chat.jsap diff --git a/client-api/sepa.jks b/client-api/sepa.jks new file mode 100644 index 0000000000000000000000000000000000000000..4b4d14d0ad13840ce7960e7ab898fe46f439e0a8 GIT binary patch literal 2317 zcmd6o={wX58^>q0%-Gkc$&g+64bj9wwlpF8n&n{ZYi0;%TFlsGElt)ajU|b)B}&=F zi0n(Yk;qyiOB|i&d9LgH3D1lB#pk}iFYfDmeXsjISUFe$fk2R>0{>IY1ot2pU-$4M zb|`om_8kO*LjW@D0N`Lk$T5LoU|9$^7z_u&$grjPQpWeNp)+?%B7Tk08!AJ^+Hq_nwwu41kD{bxSZf**5y zn>%ua<_UeH{llWfw7Q!$6^E+5QQDRLC&mT7v$%A(I-1 zLQ-3yZkI9-6U|;Mq!JuIjOHztxX(Qz&GbGrohh(0-HY?#(lNll#b+d#4SuVpwMyvh zmRn=DoEsdry$x&L3QH%wEkGBoB-it696AWclkIsx{(HRHsiI*eNV^G!l@G}AH-Tu zex=n!$LH&xN7Z0?_C_r4P*tqLEEjB^raCzd( zPBOIBrCDp{IftlHXE$5qm(HAar;>$$Y+~IId#I>5$|t|vO+_bkWK(1?fV2F9IKNuz z=l<$}fs11)x|)-pSizvfbB0@75cx;RA?;bTCz)mN{T(i1EF$ba97x@S+0-`5Q(|Pk zY6Y3Nb>uu&clwyBxa^UA<0Anjs(BN?_F8wZAXZ_fU%es~mVaE?YqvDf49V12Tz5k! zr|S90_!284`qTQmgIk;|_d*#y{Q7I}Q^p_Mg{!CAqiKm>{?L^hy1Ge;l#Y&y z-13^FH|nC44s08Vj~OG`3IHP776q%U3ob5`VH328KmWK;VNh3%b!)*OG$(%yGp-eV z(JW@7@hJqI#cYM#!zt+%TJRs|h-w)FB2 z@Fbwk-3jh4!ERm{c|h*B0Lm+FVXAFzW`x!cbq^q-O@jm7+zEsuEn53X=1p{WBZdUK z11AO9F=qi)j50=91*5ELe}q^-6~O)%{|`4NgT?-B>(SSNlEIuHfDA@J$Y3yNO`hbD zJjrK}-o5}Ej? z?;MlMy_$OAU<>C^LlQbQ_xvl#DO`krY*t@?*-7199v^|a@YQNBO`F&l<*KRU7b;CQ zgoZ*W5|o;-@4>e1n|i1j-MWevNwi{N(s@mh7n*U2Z9*qbC;Z%Zo>IuO3dG+__D6<{ z>W^Pidoa46Bia~k`Lf%QEV8;0{uFzSLhZ+y-B<&tgHHw8EYq8_p3awc)=Qe!_y3@s zrk5$ypK-Wx`z+_u``fi%HiMz60#Mu0NgZtDc!fCw0YB|uq`0=1>g$4~K%ig{_>m~^ zpQNBTU;;3{q5dueKggIB4~YoV7!%@~pIIONw~0sDJTegslB6vI!#?!&xNmy!H153S z>ynAdlEbpOvs9S=%5tqZ?8R`;T;0cs@rW-Ijjyqmq$+imsU7TW+oAyfEh2EtdQWO2 ztjeG9Q0nsH$)=U29oxuz?2jaHbD3?G_|Wwm3mpr`d@JI5X2Y*gt$LK4Do6N7BF=RS zPc3iv$jzO;4qEg3$?O?ut3EYJPqU&is)~?pPQ!I%*xieoA;*Xz_{p}FB=ak)L!N2- zi7NvcP1CV!#*el2y<5JuV`h`H@VIs!CB=zT(SmYPQbC3g89Hg=La&mLkfH=>Oi1ck zHr8}+igV5C*!78FGZm+el_AD7-4dbFa80vAG-Z>%S!3j+Qx. -*/ - -package it.unibo.arces.wot.sepa.commons.response; - -import java.text.SimpleDateFormat; -import java.util.Date; - -import com.google.gson.JsonPrimitive; - -// TODO: Auto-generated Javadoc -/** - * This class represents the ping message sent on every active websocket (see SPARQL 1.1 Subscription Language) - * - * The JSON serialization is the following: - * - * {"ping" : "yyyy-MM-dd HH:mm:ss.SSS", "spuid":"sepa://spuid/UUID"} - * - * */ - -public class Ping extends Response { - - /** - * Instantiates a new ping. - */ - public Ping(String spuid) { - super(); - - json.add("ping", new JsonPrimitive(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) ); - json.add("spuid", new JsonPrimitive(spuid)); - } - - public String getSpuid() { - return json.get("spuid").getAsString(); - } -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java index a4e121d4..657e6049 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java @@ -41,9 +41,6 @@ public boolean isJWTResponse() { public boolean isNotification() { return this.getClass().equals(Notification.class); } - public boolean isPing() { - return this.getClass().equals(Ping.class); - } public boolean isQueryResponse() { return this.getClass().equals(QueryResponse.class); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java index 802372a4..e0bed4b6 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java @@ -177,6 +177,10 @@ public String getBearerAuthorizationHeader() throws SEPASecurityException { throw new SEPASecurityException(e); } } + + public String getToken() throws SEPASecurityException { + return encryption.decrypt(getSecurityEncryptedValue("jwt")); + } /** * Gets the token type. @@ -271,6 +275,23 @@ public void setJWT(String jwt, Date expires, String type) throws SEPASecurityExc storeProperties(propertiesFile.getAbsolutePath()); } + + /** + * Sets the JWT. + * + * @param jwt + * the JSON Web Token + * @param expiresIn + * the number of seconds from now to when the token will expire + * @param type + * the token type (e.g., bearer) + * @throws SEPAPropertiesException + * @throws SEPASecurityException + * + */ + public void setJWT(String accessToken, long expiresIn, String tokenType) throws SEPASecurityException, SEPAPropertiesException { + setJWT(accessToken,new Date(new Date().getTime() + expiresIn),tokenType); + } /** * Store properties. @@ -291,5 +312,5 @@ protected void storeProperties(String propertiesFile) throws SEPAPropertiesExcep } catch (IOException e) { throw new SEPAPropertiesException(e); } - } + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java index 7497650c..603cc6e3 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java @@ -55,6 +55,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.RegistrationRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; @@ -64,6 +65,7 @@ import it.unibo.arces.wot.sepa.timing.Timings; /** + *
      * The Class SecurityManager.
      * 
      * * ### Key and Certificate Storage ###
    @@ -138,6 +140,7 @@
      * protocols:
      * 
      * SSLv3 TLSv1 TLSv1.1 TLSv1.2
    + * 
    */ public class SEPASecurityManager implements HostnameVerifier { @@ -146,12 +149,14 @@ public class SEPASecurityManager implements HostnameVerifier { /** The ssl context. */ SSLContext sslContext; - + SSLConnectionSocketFactory sslsf; - + /** The log4j2 logger. */ private static final Logger logger = LogManager.getLogger(); + private AuthenticationProperties oauthProperties = null; + /** * Instantiates a new Security Manager. * @@ -163,9 +168,10 @@ public class SEPASecurityManager implements HostnameVerifier { * the jks password * @param keyPassword * the key password - * @throws SEPASecurityException + * @throws SEPASecurityException */ - public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { + private void _SEPASecurityManager(String jksName, String jksPassword, String keyPassword, + AuthenticationProperties oauthProp) throws SEPASecurityException { // Arguments check if (jksName == null || jksPassword == null) throw new IllegalArgumentException("JKS name or password are null"); @@ -178,31 +184,53 @@ public SEPASecurityManager(String jksName, String jksPassword, String keyPasswor try { keystore = KeyStore.getInstance("JKS"); keystore.load(new FileInputStream(jksName), jksPassword.toCharArray()); - + KeyManagerFactory kmfactory = KeyManagerFactory.getInstance("SunX509"); kmfactory.init(keystore, keyPassword.toCharArray()); - + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keystore); sslContext = SSLContext.getInstance("TLSv1"); sslContext.init(kmfactory.getKeyManagers(), tmf.getTrustManagers(), null); - + // Trust own CA and all self-signed certificates and allow TLSv1 protocol only sslsf = new SSLConnectionSocketFactory(SSLContexts.custom() - .loadTrustMaterial(new File(jksName), jksPassword.toCharArray(), new TrustSelfSignedStrategy()).build(), new String[] { "TLSv1" }, null, - this); - } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException | UnrecoverableKeyException | KeyManagementException e) { + .loadTrustMaterial(new File(jksName), jksPassword.toCharArray(), new TrustSelfSignedStrategy()) + .build(), new String[] { "TLSv1" }, null, this); + } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException + | UnrecoverableKeyException | KeyManagementException e) { throw new SEPASecurityException(e); } + + oauthProperties = oauthProp; + } + + public SEPASecurityManager(AuthenticationProperties oauthProp) throws SEPASecurityException { + _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", oauthProp); + + if (oauthProp == null) + throw new IllegalArgumentException("Authorization properties are null"); } public SEPASecurityManager() throws SEPASecurityException { - this("sepa.jks","sepa2017","sepa2017"); + _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); + } + + public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { + _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); + } + + public SEPASecurityManager(String jksName, String jksPassword, String keyPassword, + AuthenticationProperties oauthProp) throws SEPASecurityException { + _SEPASecurityManager(jksName, "sepa2017", "sepa2017", oauthProp); + + if (oauthProp == null) + throw new IllegalArgumentException("Authorization properties are null"); } - + public Socket getSSLSocket() throws IOException { - return sslContext.getSocketFactory().createSocket(); + return sslContext.getSocketFactory().createSocket(); } public SSLContext getSSLContext() { @@ -224,31 +252,66 @@ public boolean verify(String hostname, SSLSession session) { return true; } - // Registration to the Authorization Server (AS) + /** + * Register the identity and store the credentials into the Authentication properties + * @throws SEPAPropertiesException + * @throws SEPASecurityException + */ + public Response register(String identity) throws SEPASecurityException, SEPAPropertiesException { + Response ret = register(oauthProperties.getRegisterUrl(), identity); + + if (ret.isError()) return ret; + + RegistrationResponse reg = (RegistrationResponse) ret; + oauthProperties.setCredentials(reg.getClientId(), reg.getClientSecret()); + + return ret; + } + + // Get the authorization header (by requesting a new token first) + public String getAuthorizationHeader() throws SEPAPropertiesException, SEPASecurityException { + Response ret = requestToken(oauthProperties.getTokenRequestUrl(), + oauthProperties.getBasicAuthorizationHeader()); + + if (ret.isError()) { + if (((ErrorResponse) ret).getErrorCode() != 400) + throw new SEPASecurityException(new IllegalArgumentException("Failed to get a new token")); + return oauthProperties.getBearerAuthorizationHeader(); + } + + JWTResponse token = (JWTResponse) ret; + oauthProperties.setJWT(token.getAccessToken(), token.getExpiresIn(), token.getTokenType()); + + return oauthProperties.getBearerAuthorizationHeader(); + } + + /** + * Request registration of "entity" at the Authorization Server listening at the specified URL + * */ public Response register(String url, String identity) { logger.debug("REGISTER " + identity); CloseableHttpResponse response = null; - long start = Timings.getTime(); - + long start = Timings.getTime(); + try { URI uri = new URI(url); ByteArrayEntity body = new ByteArrayEntity(new RegistrationRequest(identity).toString().getBytes("UTF-8")); HttpPost httpRequest = new HttpPost(uri); httpRequest.setHeader("Content-Type", "application/json"); - httpRequest.setHeader("Accept", "application/json"); + httpRequest.setHeader("Accept", "application/json"); httpRequest.setEntity(body); - + logger.trace(httpRequest); - + response = getSSLHttpClient().execute(httpRequest); - + logger.debug("Response: " + response); HttpEntity entity = response.getEntity(); String jsonResponse = EntityUtils.toString(entity, Charset.forName("UTF-8")); EntityUtils.consume(entity); JsonObject json = new JsonParser().parse(jsonResponse).getAsJsonObject(); - + if (json.has("error")) { Timings.log("REGISTER_ERROR", start, Timings.getTime()); ErrorResponse error = new ErrorResponse(0, json.get("error").getAsJsonObject().get("code").getAsInt(), @@ -256,20 +319,18 @@ public Response register(String url, String identity) { logger.error(error); return error; } - + String id = json.get("credentials").getAsJsonObject().get("client_id").getAsString(); String secret = json.get("credentials").getAsJsonObject().get("client_secret").getAsString(); JsonElement signature = json.get("credentials").getAsJsonObject().get("signature"); Timings.log("REGISTER", start, Timings.getTime()); return new RegistrationResponse(id, secret, signature); - - } - catch(Exception e) { + + } catch (Exception e) { logger.error(e.getMessage()); Timings.log("REGISTER_ERROR", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - finally { + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } finally { try { if (response != null) response.close(); @@ -280,56 +341,77 @@ public Response register(String url, String identity) { } } } - - // Token request to the Authorization Server (AS) - public Response requestToken(String url,String authorization) { + + /** + * It is used to request a new token. + * + * @param url + * the url of the Authorization Server + * @param authorization + * the header to be sent (e.g., "Basic authorization header") + * + * @return ErrorResponse in case of error. The error codes correspond to an HTTP + * status codes as follows: SC_UNAUTHORIZED (401) : client not + * authorized, SC_BAD_REQUEST (400) : the token is not expired, + * SC_INTERNAL_SERVER_ERROR (500) : error validating the token + * @see ErrorResponse + * @see JWTResponse + */ + + public Response requestToken(String url, String authorization) { logger.debug("TOKEN_REQUEST"); CloseableHttpResponse response = null; - long start = Timings.getTime(); - + long start = Timings.getTime(); + try { URI uri = new URI(url); - + HttpPost httpRequest = new HttpPost(uri); httpRequest.setHeader("Content-Type", "application/json"); - httpRequest.setHeader("Accept", "application/json"); + httpRequest.setHeader("Accept", "application/json"); httpRequest.setHeader("Authorization", authorization); - + response = getSSLHttpClient().execute(httpRequest); - + logger.debug("Response: " + response); HttpEntity entity = response.getEntity(); String jsonResponse = EntityUtils.toString(entity, Charset.forName("UTF-8")); EntityUtils.consume(entity); + + // Parse response JsonObject json = new JsonParser().parse(jsonResponse).getAsJsonObject(); - + if (json.has("error")) { - Timings.log("TOKEN_REQUEST_ERROR", start, Timings.getTime()); + Timings.log("TOKEN_REQUEST", start, Timings.getTime()); + + //TOKEN IS NOT EXPIRED + if (json.get("error").getAsJsonObject().get("code").getAsInt()==400 && oauthProperties != null) { + return new JWTResponse(oauthProperties.getToken(),oauthProperties.getTokenType(),oauthProperties.getExpiringSeconds()); + } + ErrorResponse error = new ErrorResponse(0, json.get("error").getAsJsonObject().get("code").getAsInt(), json.get("error").getAsJsonObject().get("body").getAsString()); logger.error(error); return error; } - + int seconds = json.get("token").getAsJsonObject().get("expires_in").getAsInt(); String jwt = json.get("token").getAsJsonObject().get("access_token").getAsString(); String type = json.get("token").getAsJsonObject().get("token_type").getAsString(); - - return new JWTResponse(jwt, type, seconds); - } - catch(Exception e) { + + return new JWTResponse(jwt, type, seconds); + } catch (Exception e) { logger.error(e.getMessage()); - Timings.log("TOKEN_REQUEST_ERROR", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - finally { + Timings.log("TOKEN_REQUEST", start, Timings.getTime()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } finally { try { if (response != null) response.close(); } catch (IOException e) { logger.error(e.getMessage()); - Timings.log("TOKEN_REQUEST_ERROR", start, Timings.getTime()); + Timings.log("TOKEN_REQUEST", start, Timings.getTime()); return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index 03a8ffa9..4ffbe87e 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -14,7 +14,7 @@ public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEP if( configuaration != null){ result = new JSAP(configuaration); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); + URL config = Thread.currentThread().getContextClassLoader().getResource("chat.jsap"); result = new JSAP(config.getPath()); } return result; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java index 72777da7..c1eaa96e 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java @@ -1,7 +1,9 @@ package it.unibo.arces.wot.sepa.api; import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; @@ -25,19 +27,21 @@ public class ITProtocolTest { - private static HashMap updates = new HashMap(); - private static HashMap queries = new HashMap(); - private static HashMap subscribes = new HashMap(); - private static HashMap protocols = new HashMap(); + protected static HashMap updates = new HashMap(); + protected static HashMap queries = new HashMap(); + protected static HashMap subscribes = new HashMap(); + + protected static HashMap protocols = new HashMap(); - private static JSAP properties = null; + protected static JSAP properties = null; - private static final MockSubscriptionHandler subHandler = new MockSubscriptionHandler();; - - private static SPARQL11Protocol client = null; + protected static final MockSubscriptionHandler subHandler = new MockSubscriptionHandler();; + protected static SPARQL11Protocol client = null; + protected static SPARQL11SEProtocol seClient = null; + @BeforeClass - public static void start() throws Exception { + public static void init() throws Exception { properties = ConfigurationProvider.GetTestEnvConfiguration(); for (String id : properties.getQueryIds()) { @@ -53,73 +57,66 @@ public static void start() throws Exception { } @AfterClass - public static void stop() throws IOException { + public static void dispose() throws IOException { } @Before - public void beginTest() throws IOException { + public void beginTest() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { client = new SPARQL11Protocol(); + seClient = new SPARQL11SEProtocol(protocols.get("Q2"), subHandler); + final Response ret = client.update(updates.get("DELETE_ALL")); + assertFalse(String.valueOf(ret), ret.isError()); } @After public void endTest() throws IOException { - if (client != null) { - client.close(); - } + if (client != null) client.close(); + if (seClient != null) seClient.close(); } @Test(timeout = 5000) public void Update() throws IOException { - client = new SPARQL11Protocol(); final Response ret = client.update(updates.get("U1")); assertFalse(String.valueOf(ret), ret.isError()); } @Test(timeout = 5000) public void Query() throws IOException { - client = new SPARQL11Protocol(); final Response ret = client.query(queries.get("Q1")); assertFalse(String.valueOf(ret), ret.isError()); } @Test(timeout = 5000) public void UpdateAndQueryOneResult() throws IOException { - client = new SPARQL11Protocol(); final Response updateRes = client.update(updates.get("U2")); final Response queryRes = client.query(queries.get("Q2")); - if (updateRes.isError()) - assertFalse("Update: " + String.valueOf(updateRes), true); - else if (queryRes.isError()) - assertFalse(" Query: " + String.valueOf(queryRes), true); - else - assertFalse(String.valueOf(queryRes), ((QueryResponse) queryRes).getBindingsResults().size() != 1); + if (updateRes.isError()) assertFalse("Update: " + String.valueOf(updateRes), true); + if (queryRes.isError()) assertFalse(" Query: " + String.valueOf(queryRes), true); + + assertFalse(String.valueOf(queryRes), ((QueryResponse) queryRes).getBindingsResults().size() != 1); } @Test(timeout = 5000) public void SubscribeAndNotifyOneAddedResult() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException { - client = new SPARQL11SEProtocol(protocols.get("Q2"), subHandler); - final Response subRes = ((SPARQL11SEProtocol) client).subscribe(subscribes.get("Q2")); - final Response updateRes = client.update(updates.get("U2")); + final Response subRes = seClient.subscribe(subscribes.get("Q2")); + final Response updateRes = seClient.update(updates.get("U2")); Response notify = subHandler.getResponse(); - if (subRes.isError()) - assertFalse(" Subscribe: " + String.valueOf(subRes), true); - else if (updateRes.isError()) - assertFalse("Update: " + String.valueOf(updateRes), true); - else if (notify.isError()) - assertFalse(String.valueOf(notify), true); - else if (((Notification)notify).getARBindingsResults().getAddedBindings().size() != 1) assertFalse(String.valueOf(notify), true); - + if (subRes.isError()) assertFalse(" Subscribe: " + String.valueOf(subRes), true); + if (updateRes.isError()) assertFalse("Update: " + String.valueOf(updateRes), true); + if (notify.isError()) assertFalse(String.valueOf(notify), true); + + if (((Notification)notify).getARBindingsResults().getAddedBindings().size() != 1) assertFalse(String.valueOf(notify), true); } - private static UpdateRequest getUpdateRequest(String id, int timeout, String authorization) { + protected static UpdateRequest getUpdateRequest(String id, int timeout, String authorization) { HTTPMethod method = properties.getUpdateMethod(id); String scheme = properties.getUpdateProtocolScheme(id); String host = properties.getUpdateHost(id); @@ -133,7 +130,7 @@ private static UpdateRequest getUpdateRequest(String id, int timeout, String aut authorization); } - private static QueryRequest getQueryRequest(String id, int timeout, String authorization) { + protected static QueryRequest getQueryRequest(String id, int timeout, String authorization) { HTTPMethod method = properties.getQueryMethod(id); String scheme = properties.getQueryProtocolScheme(id); String host = properties.getQueryHost(id); @@ -147,7 +144,7 @@ private static QueryRequest getQueryRequest(String id, int timeout, String autho authorization); } - private static SubscribeRequest getSubscribeRequest(String id, String authorization) { + protected static SubscribeRequest getSubscribeRequest(String id, String authorization) { String sparql = properties.getSPARQLQuery(id); String graphUri = properties.getDefaultGraphURI(id); String namedGraphUri = properties.getNamedGraphURI(id); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java index 6aec7fef..07c60c63 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java @@ -2,62 +2,65 @@ import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; +import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import it.unibo.arces.wot.sepa.pattern.JSAP; import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.BeforeClass; -import java.net.URL; +import java.io.IOException; import static org.junit.Assert.*; -public class ITSecureProtocolTest { - - private SPARQL11SEProtocol client; - private MockSubscriptionHandler subHandler; - - private final static String VALID_ID = "SEPATest"; - private final static String NOT_VALID_ID = "RegisterMePlease"; - private JSAP properties; - - private SEPASecurityManager sm ; - - @Before - public void setUp() throws Exception { - URL config = Thread.currentThread().getContextClassLoader().getResource("dev.jsap"); - properties = new JSAP(config.getPath()); - subHandler = new MockSubscriptionHandler(); - - sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017"); - - ISubscriptionProtocol protocol = null; - - protocol = new WebSocketSubscriptionProtocol(properties.getSubscribeHost(null), - properties.getSubscribePort(null), properties.getSubscribePath(null),sm); - - client = new SPARQL11SEProtocol(protocol, subHandler); - } - - @Test - public void Register() throws SEPASecurityException, SEPAPropertiesException{ - Response response; - response = sm.register(properties.getAuthenticationProperties().getRegisterUrl(),NOT_VALID_ID); - assertTrue("Accepted not valid ID",response.isError()); - - response = sm.register(properties.getAuthenticationProperties().getRegisterUrl(),VALID_ID); - assertTrue("Not accepted valid ID",response.isError()); - } - - @Test - @Ignore - public void RequestToken() throws SEPASecurityException, SEPAPropertiesException { - Response response; - response = sm.requestToken(properties.getAuthenticationProperties().getTokenRequestUrl(),properties.getAuthenticationProperties().getBasicAuthorizationHeader()); - assertFalse(String.valueOf(response),response.isError()); - - assertFalse("SEPA returned an expired token",properties.getAuthenticationProperties().isTokenExpired()); - } +public class ITSecureProtocolTest extends ITProtocolTest { + private final static String VALID_ID = "SEPATest"; + private final static String NOT_VALID_ID = "RegisterMePlease"; + + private static SEPASecurityManager sm; + + @BeforeClass + public static void init() throws Exception { + properties = ConfigurationProvider.GetTestEnvConfiguration(); + + sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", + new AuthenticationProperties(properties.getFileName())); + + // Registration + Response response = sm.register(NOT_VALID_ID); + assertFalse("Failed to register a not valid ID", !response.isError()); + response = sm.register(VALID_ID); + assertFalse("Failed to register a valid ID", response.isError()); + + for (String id : properties.getQueryIds()) { + queries.put(id, getQueryRequest(id, 5000, sm.getAuthorizationHeader())); + subscribes.put(id, getSubscribeRequest(id, sm.getAuthorizationHeader())); + protocols.put(id, new WebSocketSubscriptionProtocol(properties.getSubscribeHost(id), + properties.getSubscribePort(id), properties.getSubscribePath(id),sm)); + } + + for (String id : properties.getUpdateIds()) { + updates.put(id, getUpdateRequest(id, 5000, sm.getAuthorizationHeader())); + } + } + + @Before + public void beginTest() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { + client = new SPARQL11Protocol(sm); + seClient = new SPARQL11SEProtocol(protocols.get("Q2"), subHandler,sm); + + // Set authorization header + for (UpdateRequest req : updates.values()) req.setAuthorizationHeader(sm.getAuthorizationHeader()); + for (QueryRequest req : queries.values()) req.setAuthorizationHeader(sm.getAuthorizationHeader()); + for (SubscribeRequest req : subscribes.values()) req.setAuthorizationHeader(sm.getAuthorizationHeader()); + + final Response ret = client.update(updates.get("DELETE_ALL")); + + assertFalse(String.valueOf(ret), ret.isError()); + } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/TestQueries.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/TestQueries.java deleted file mode 100644 index d5d7ee6b..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/TestQueries.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -public class TestQueries { - //String concatenation it's really bad for the performance. But since this is a test I'd prefer code readability - static final String SIMPLE_UPDATE = "prefix test: " + - "insert data {test:Sub test:Pred \"測試\"} "; - - static final String NOTIF_UPDATE = "prefix test: " + - "insert data {test:Sub test:hasNotification \"Hello there!\"} "; - - static final String SIMPLE_QUERY = "prefix test: " + - "select ?s ?p ?o " + - "where {?s ?p ?o}"; - - static final String UTF8_RESULT_QUERY = "prefix test: " + - "select ?s ?p ?o " + - "where {test:Sub test:Pred ?o}"; - - static final String NOTIF_QUERY = "prefix test: " + - "select ?s ?p ?o " + - "where {test:Sub test:hasNotification ?o}"; -} diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index 300828aa..46cb2a66 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -1,7 +1,7 @@ { "host": "localhost", "oauth": { - "enable" : true, + "enable": true, "register": "https://localhost:8443/oauth/register", "tokenRequest": "https://localhost:8443/oauth/token" }, @@ -42,72 +42,10 @@ "sepa": "http://wot.arces.unibo.it/sepa#", "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, - "extended": { - "notificationMaxDelay": 2000, - "sequence": [ - { - "id": "RegisterMePlease!", - "name": "Register not allowed", - "type": "REGISTER", - "passed": false - }, - { - "id": "SEPATest", - "name": "Register allowed", - "type": "REGISTER", - "passed": true - }, - { - "name": "Update (secure)", - "type": "UPDATE", - "id": "U1", - "passed": true - }, - { - "name": "Query (secure)", - "type": "QUERY", - "id": "Q1", - "results": 1, - "passed": true - }, - { - "name": "Subscribe (secure)", - "type": "SUBSCRIBE", - "id": "Q1", - "results": 1, - "passed": true - }, - { - "name": "Update (secure)", - "type": "UPDATE", - "id": "U2", - "secure": true, - "passed": true - }, - { - "name": "Notification", - "type": "WAIT_NOTIFICATION", - "passed": true - }, - { - "name": "Unsubscribe (secure)", - "type": "UNSUBSCRIBE", - "passed": true - }, - { - "name": "Update (secure)", - "type": "UPDATE", - "id": "U2", - "passed": true - }, - { - "name": "Notification (not received)", - "type": "WAIT_NOTIFICATION", - "passed": false - } - ] - }, "updates": { + "DELETE_ALL" : { + "sparql" : "delete where {?x ?y ?z}" + }, "U1": { "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" }, @@ -118,6 +56,9 @@ "queries": { "Q1": { "sparql": "select * where {?x ?y \"ვაიმეე\"}" + }, + "Q2": { + "sparql": "select * where {?x ?y ?z}" } } -} \ No newline at end of file +} diff --git a/client-api/src/test/resources/sepatest.jsap b/client-api/src/test/resources/sepatest.jsap index 1733ac45..72708ffa 100644 --- a/client-api/src/test/resources/sepatest.jsap +++ b/client-api/src/test/resources/sepatest.jsap @@ -1,12 +1,12 @@ { "host": "localhost", "oauth": { - "enable" : false, + "enable" : true, "register": "https://localhost:8443/oauth/register", "tokenRequest": "https://localhost:8443/oauth/token" }, "sparql11protocol": { - "protocol": "http", + "protocol": "https", "port": 8000, "query": { "path": "/query", @@ -20,7 +20,7 @@ } }, "sparql11seprotocol": { - "protocol": "ws", + "protocol": "wss", "availableProtocols": { "ws": { "port": 9000, @@ -42,58 +42,6 @@ "sepa": "http://wot.arces.unibo.it/sepa#", "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, - "extended": { - "notificationMaxDelay": 2000, - "sequence": [ - { - "name": "Update", - "type": "UPDATE", - "id": "U1", - "passed": true - }, - { - "name": "Query", - "type": "QUERY", - "id": "Q1", - "results": 1, - "passed": true - }, - { - "name": "Subscribe", - "type": "SUBSCRIBE", - "id": "Q1", - "results": 1, - "passed": true - }, - { - "name": "Update", - "type": "UPDATE", - "id": "U2", - "passed": true - }, - { - "name": "Notification", - "type": "WAIT_NOTIFICATION", - "passed": true - }, - { - "name": "Unsubscribe", - "type": "UNSUBSCRIBE", - "passed": true - }, - { - "name": "Update", - "type": "UPDATE", - "id": "U2", - "passed": true - }, - { - "name": "Notification (not received)", - "type": "WAIT_NOTIFICATION", - "passed": false - } - ] - }, "updates": { "DELETE_ALL" : { "sparql" : "delete where {?x ?y ?z}" diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java index d068d720..5bc0d3b8 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java @@ -208,7 +208,6 @@ private boolean init(KeyStore keyStore,String keyAlias,String keyPwd) throws Key public AuthorizationManager(String keystoreFileName,String keystorePwd,String keyAlias,String keyPwd,String certificate) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, JOSEException, SEPASecurityException { SEPABeans.registerMBean("SEPA:type=AuthorizationManager",this); - //sManager = new SSLSecurityManager(keystoreFileName, keystorePwd, keyAlias, keyPwd, certificate,false,true,null); sManager = new SEPASecurityManager(keystoreFileName, keystorePwd,keyPwd); init(sManager.getKeyStore(),keyAlias, keyPwd); @@ -353,8 +352,12 @@ public Response register(String identity) { * { * "code": Error code, * "body": "Error details" - * * } + * + * The error codes correspond to an HTTP status codes as follows: + * --- SC_UNAUTHORIZED (401) : client not authorized + * --- SC_BAD_REQUEST (400) : the token is not expired + * --- SC_INTERNAL_SERVER_ERROR (500) : error validating the token * */ public Response getToken(String encodedCredentials) { logger.debug("Get token"); @@ -556,13 +559,6 @@ public Response validateToken(String accessToken) { return new JWTResponse(accessToken,"bearer",now.getTime()-claimsSet.getExpirationTime().getTime()); } - - /* - public SSLEngineConfigurator getWssConfigurator() { - SSLEngineConfigurator config = new SSLEngineConfigurator(sManager.getWssConfigurator().getSslContext(), false, false, false); - return config; - } -*/ @Override public long getTokenExpiringPeriod() { diff --git a/tools/pom.xml b/tools/pom.xml index b90a8d86..cb256c45 100644 --- a/tools/pom.xml +++ b/tools/pom.xml @@ -1,12 +1,19 @@ - - 4.0.0 - - it.unibo.arces.wot - sepa - ${revision} - - tools + + 4.0.0 + + it.unibo.arces.wot + sepa + ${revision} + + tools + + junit + junit + 4.11 + test + org.apache.logging.log4j diff --git a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java new file mode 100644 index 00000000..b51b8e0c --- /dev/null +++ b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java @@ -0,0 +1,22 @@ +package it.unibo.arces.wot.sepa.apps.chat; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import java.net.URL; + +public class ConfigurationProvider { + + public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { + JSAP result; + final String configuaration = System.getProperty("testConfiguration"); + if( configuaration != null){ + result = new JSAP(configuaration); + }else{ + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest-secure.jsap"); + result = new JSAP(config.getPath()); + } + return result; + } +} diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java similarity index 58% rename from tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java rename to tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java index 8385eec6..75758902 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java +++ b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java @@ -6,34 +6,41 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.BeforeClass; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.pattern.JSAP; +import org.junit.Test; + +import static org.junit.Assert.*; + public class SEPAChatTest { private static final Logger logger = LogManager.getLogger(); - private int N_CLIENTS = 2; - private int BASE = 0; - private int MESSAGES = 10; - private Users users; + private static int N_CLIENTS = 2; + private static int BASE = 0; + private static int MESSAGES = 10; + private static Users users; private static List clients = new ArrayList(); - - private enum CLIENT_TYPE { + + private static enum CLIENT_TYPE { PING_PONG, BASIC }; - private CLIENT_TYPE type = CLIENT_TYPE.BASIC; + private static CLIENT_TYPE type = CLIENT_TYPE.BASIC; - public SEPAChatTest() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { + @BeforeClass + public static void init() { try { - JSAP app = new JSAP("chat.jsap"); + JSAP app = ConfigurationProvider.GetTestEnvConfiguration(); + BASE = app.getExtendedData().get("base").getAsInt(); N_CLIENTS = app.getExtendedData().get("clients").getAsInt(); String sType = app.getExtendedData().get("type").getAsString(); - switch(sType.toUpperCase()) { + switch (sType.toUpperCase()) { case "BASIC": type = CLIENT_TYPE.BASIC; MESSAGES = app.getExtendedData().get("messages").getAsInt(); @@ -45,69 +52,66 @@ public SEPAChatTest() throws SEPAProtocolException, SEPAPropertiesException, SEP } catch (Exception e) { logger.error(e.getMessage()); } + } - DeleteAll client = new DeleteAll(); - client.clean(); - try { - client.close(); - } catch (IOException e) { - logger.error(e.getMessage()); - } + @Test + public void chatTest() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException { + deleteAllClients(); + registerClients(); - // Register chat BOTS - UserRegistration registration = new UserRegistration(); - for (int i = BASE; i < BASE + N_CLIENTS; i++) { - registration.register("ChatBot" + i); - } - try { - registration.close(); - } catch (IOException e) { - logger.error(e.getMessage()); - } - users = new Users(); - } - - public boolean start() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { - if (!users.joinChat()) - return false; - + assertFalse("Users failed to join chat",!users.joinChat()); + for (String user : users.getUsers()) { ChatClient client = null; switch (type) { case BASIC: - client = new BasicClient(user, users,MESSAGES); + client = new BasicClient(user, users, MESSAGES); break; case PING_PONG: client = new PingPongClient(user, users); break; default: - client = new BasicClient(user, users,MESSAGES); + client = new BasicClient(user, users, MESSAGES); } Thread th = new Thread(client); clients.add(th); th.start(); } - - return true; + + for (Thread th : clients) + th.join(60000); + + assertTrue("Chat tested",true); } - public static void main(String[] args) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException, InterruptedException { + @Test + public void deleteAllClients() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { + DeleteAll client = new DeleteAll(); + client.clean(); + try { + client.close(); + } catch (IOException e) { + assertFalse(e.getMessage(), true); + } - SEPAChatTest chat = new SEPAChatTest(); + assertTrue("Delete all clients SUCCESS", true); + } + @Test + public void registerClients() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { + // Register chat BOTS + UserRegistration registration = new UserRegistration(); + for (int i = BASE; i < BASE + N_CLIENTS; i++) { + registration.register("ChatBot" + i); + } try { - if (!chat.start()) - System.exit(-1); - } catch (Exception e) { - logger.error(e.getMessage()); - System.exit(-1); + registration.close(); + } catch (IOException e) { + assertFalse(e.getMessage(), true); } - - for (Thread th:clients) th.join(60000); - System.exit(0); + assertTrue("Clients registration SUCCESS", true); } } diff --git a/tools/src/test/resources/chat.jsap b/tools/src/test/resources/chat.jsap new file mode 100644 index 00000000..816c7c68 --- /dev/null +++ b/tools/src/test/resources/chat.jsap @@ -0,0 +1,189 @@ +{ + "host": "localhost", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "extended": { + "type": "basic", + "base": 0, + "clients": 10, + "messages": 1 + }, + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/chat", + "named-graph-uri": "http://wot.arces.unibo.it/chat", + "using-graph-uri": "http://wot.arces.unibo.it/chat", + "using-named-graph-uri": "http://wot.arces.unibo.it/chat" + }, + "namespaces": { + "schema": "http://schema.org/", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "chat": "http://wot.arces.unibo.it/chat#" + }, + "updates": { + "SEND": { + "sparql": "INSERT {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?time} WHERE {?sender rdf:type schema:Person . ?receiver rdf:type schema:Person BIND(STR(now()) AS ?time) BIND(IRI(CONCAT(\"http://schema.org/Message-\",STRUUID())) AS ?message)}", + "forcedBindings": { + "text": { + "type": "literal", + "value": "Ciao!" + }, + "sender": { + "type": "uri", + "value": "chat:IamASender" + }, + "receiver": { + "type": "uri", + "value": "chat:IamAReceiver" + } + } + }, + "SET_RECEIVED": { + "sparql": "INSERT {?message schema:dateReceived ?time} WHERE {?message rdf:type schema:Message BIND(STR(now()) AS ?time)}", + "forcedBindings": { + "message": { + "type": "uri", + "value": "chat:ThisIsAMessage" + } + } + }, + "REMOVE": { + "sparql": "DELETE {?message ?p ?o} WHERE {?message rdf:type schema:Message ; ?p ?o}", + "forcedBindings": { + "message": { + "type": "uri", + "value": "chat:ThisIsAMessage" + } + } + }, + "STORE_SENT": { + "sparql": "INSERT DATA {?message schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?dateSent}", + "forcedBindings": { + "dateSent": { + "type": "literal", + "value": "2018-06-28T00:00:00", + "datatype" : "xsd:dateTime" + }, + "message": { + "type": "uri", + "value": "chat:ThisIsAMessage" + }, + "text": { + "type": "literal", + "value": "A message to be stored" + }, + "sender": { + "type": "uri", + "value": "chat:IAmASender" + }, + "receiver": { + "type": "uri", + "value": "chat:IAmAReceiver" + } + }, + "graphs": { + "using-graph-uri": "http://wot.arces.unibo.it/chat/log", + "using-named-graph-uri": "http://wot.arces.unibo.it/chat/log" + } + }, + "STORE_RECEIVED": { + "sparql": "INSERT DATA {?message schema:dateReceived ?dateReceived}", + "forcedBindings": { + "dateReceived": { + "type": "literal", + "value": "2018-06-28T00:00:00", + "datatype" : "xsd:dateTime" + }, + "message": { + "type": "uri", + "value": "chat:ThisIsAMessage" + } + }, + "graphs": { + "using-graph-uri": "http://wot.arces.unibo.it/chat/log", + "using-named-graph-uri": "http://wot.arces.unibo.it/chat/log" + } + }, + "REGISTER_USER": { + "sparql": "DELETE {?x rdf:type schema:Person . ?x schema:name ?userName} WHERE {?x rdf:type schema:Person . ?x schema:name ?userName} ; INSERT {?id rdf:type schema:Person ; schema:name ?userName} WHERE {BIND(IRI(CONCAT(\"http://schema.org/Person-\",STRUUID())) AS ?id)}", + "forcedBindings": { + "userName": { + "type": "literal", + "value": "My user name" + } + } + }, + "DELETE_ALL": { + "sparql": "delete {?s ?p ?o} where {?s ?p ?o}" + } + }, + "queries": { + "SENT": { + "sparql": "SELECT ?message ?sender ?name ?text ?time WHERE {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver ; schema:dateSent ?time . ?sender rdf:type schema:Person ; schema:name ?name . ?receiver rdf:type schema:Person} ORDER BY ?time", + "forcedBindings": { + "receiver": { + "type": "uri", + "value": "chat:IAmAReceiver" + } + } + }, + "RECEIVED": { + "sparql": "SELECT ?message ?time WHERE {?message schema:sender ?sender ; schema:dateReceived ?time ; rdf:type schema:Message}", + "forcedBindings": { + "sender": { + "type": "uri", + "value": "chat:IAmASender" + } + } + }, + "LOG_SENT": { + "sparql": "SELECT ?message ?sender ?receiver ?text ?dateSent WHERE {?message schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?dateSent}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/chat/log", + "named-graph-uri": "http://wot.arces.unibo.it/chat/log" + } + }, + "LOG_RECEIVED": { + "sparql": "SELECT ?message ?dateReceived WHERE {?message schema:dateReceived ?dateReceived}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/chat/log", + "named-graph-uri": "http://wot.arces.unibo.it/chat/log" + } + }, + "USERS": { + "sparql": "SELECT ?user ?userName WHERE {?user rdf:type schema:Person ; schema:name ?userName}" + }, + "QUERY_ALL": { + "sparql": "select * where {?s ?p ?o}" + } + } +} From 88b845d0c1e483afe68b9059c1203731e7354c6a Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 13 Jul 2018 09:01:33 +0200 Subject: [PATCH 33/76] Version 0.9.5 tested with JUnit --- .../wot/sepa/api/ISubscriptionProtocol.java | 2 +- .../wot/sepa/api/SPARQL11SEProtocol.java | 4 +- .../websocket/SEPAWebsocketClient.java | 91 ++- .../websocket/SPARQL11SESecureWebsocket.java | 34 +- .../websocket/SPARQL11SEWebsocket.java | 160 +--- .../WebSocketSubscriptionProtocol.java | 43 +- .../exceptions/SEPAProtocolException.java | 4 + .../commons/protocol/SPARQL11Protocol.java | 370 +++++---- .../sepa/commons/request/QueryRequest.java | 17 +- .../wot/sepa/commons/request/Request.java | 4 +- .../sepa/commons/request/UpdateRequest.java | 19 +- .../commons/response/SubscribeResponse.java | 1 - .../commons/security/SEPASecurityManager.java | 2 +- .../unibo/arces/wot/sepa/timing/Timings.java | 2 +- .../wot/sepa/api/ConfigurationProvider.java | 2 +- .../arces/wot/sepa/api/ITProtocolTest.java | 154 ---- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 255 ++++++ .../wot/sepa/api/ITSecureProtocolTest.java | 66 -- .../unibo/arces/wot/sepa/api/Publisher.java | 65 ++ .../unibo/arces/wot/sepa/api/Subscriber.java | 122 +++ .../websocket/ITSEPAWebsocketClient.java | 118 +++ client-api/src/test/resources/sepatest.jsap | 28 +- .../wot/sepa/engine/bean/EngineBeans.java | 138 +++- .../sepa/engine/bean/HTTPHandlerBeans.java | 43 +- .../wot/sepa/engine/bean/ProcessorBeans.java | 140 +--- .../sepa/engine/bean/QueryProcessorBeans.java | 90 ++ .../engine/bean/SubscribeProcessorBeans.java | 49 +- .../engine/bean/UpdateProcessorBeans.java | 102 +++ .../wot/sepa/engine/bean/WebsocketBeans.java | 10 +- .../arces/wot/sepa/engine/core/Engine.java | 110 ++- .../wot/sepa/engine/core/EngineMBean.java | 35 +- .../sepa/engine/core/EngineProperties.java | 50 +- .../dependability/DependabilityManager.java | 58 +- .../engine/processing/ProcessorMBean.java | 26 - .../{Processor.java => ProcessorThread.java} | 177 ++-- .../processing/ProcessorThreadMBean.java | 11 + .../engine/processing/QueryProcessor.java | 73 +- .../processing/QueryProcessorMBean.java | 21 + .../engine/processing/SpuKillerThread.java | 35 + .../engine/processing/SubscribeProcessor.java | 177 ++-- .../processing/SubscribeProcessorMBean.java | 10 +- .../processing/UpdateProcessingThread.java | 66 ++ .../engine/processing/UpdateProcessor.java | 74 +- .../processing/UpdateProcessorMBean.java | 20 + .../processing/subscriptions/SPUManager.java | 2 +- .../sepa/engine/protocol/http/HttpGate.java | 12 - .../sepa/engine/protocol/http/HttpsGate.java | 23 - .../protocol/http/handler/QueryHandler.java | 101 +-- .../http/handler/SPARQL11Handler.java | 110 ++- .../protocol/http/handler/UpdateHandler.java | 100 +-- .../websocket/SecureWebsocketServer.java | 4 +- .../websocket/WebsocketEventHandler.java | 19 +- .../protocol/websocket/WebsocketServer.java | 108 +-- .../wot/sepa/engine/scheduling/Scheduler.java | 8 +- .../subscription/SPUMangerTest.java | 14 +- tools/arces-demo.jsap | 77 -- tools/mqtt-topics.jsap | 392 +++++++++ tools/mqtt.jsap | 766 ------------------ .../unibo/arces/wot/sepa/tools/Dashboard.java | 314 ++++--- .../arces/wot/sepa/tools/SEPATestClient.java | 381 --------- tools/{ => src/main/resources}/chat.jsap | 0 tools/src/main/resources/log4j2.xml | 4 +- .../sepa/apps/chat/ConfigurationProvider.java | 2 +- 63 files changed, 2694 insertions(+), 2821 deletions(-) delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/QueryProcessorBeans.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/UpdateProcessorBeans.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/{Processor.java => ProcessorThread.java} (56%) create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessorMBean.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessorMBean.java create mode 100644 tools/mqtt-topics.jsap delete mode 100644 tools/mqtt.jsap delete mode 100644 tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java rename tools/{ => src/main/resources}/chat.jsap (100%) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java index 6bde1869..42c52210 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java @@ -7,7 +7,7 @@ public interface ISubscriptionProtocol { - void setHandler(ISubscriptionHandler handler) throws SEPAProtocolException; + boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException; void close(); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java index 757c6e53..02f7b231 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java @@ -56,7 +56,7 @@ public SPARQL11SEProtocol(ISubscriptionProtocol protocol,ISubscriptionHandler ha if (!protocol.isSecure()) throw new SEPAProtocolException(new IllegalArgumentException("Mixing secure and not secure protocols is not allowed")); this.subscriptionProtocol = protocol; - this.subscriptionProtocol.setHandler(handler); + this.subscriptionProtocol.connect(handler); } public SPARQL11SEProtocol(ISubscriptionProtocol protocol,ISubscriptionHandler handler) throws IllegalArgumentException, SEPAProtocolException { @@ -68,7 +68,7 @@ public SPARQL11SEProtocol(ISubscriptionProtocol protocol,ISubscriptionHandler ha if (protocol.isSecure()) throw new SEPAProtocolException(new IllegalArgumentException("Security parameters are missing")); this.subscriptionProtocol = protocol; - this.subscriptionProtocol.setHandler(handler); + this.subscriptionProtocol.connect(handler); } public boolean isSecure() { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java index 44bf9bbc..d4a87720 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java @@ -1,10 +1,12 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.net.URI; +import java.util.concurrent.Semaphore; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.java_websocket.client.WebSocketClient; +import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.java_websocket.handshake.ServerHandshake; import com.google.gson.JsonObject; @@ -18,10 +20,11 @@ import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; public class SEPAWebsocketClient extends WebSocketClient { - protected static final Logger logger = LogManager.getLogger(); + protected final Logger logger = LogManager.getLogger(); private ISubscriptionHandler handler; private Response response = new ErrorResponse(500, "null"); + private Semaphore mutex = new Semaphore(0); private boolean responseReceived = false; public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { @@ -35,46 +38,66 @@ public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { this.handler = handler; } - private synchronized Response waitResponse(long timeout) { - if (!responseReceived) - try { - wait(timeout); - } catch (InterruptedException e) { - return new ErrorResponse(500, e.getMessage()); + private Response waitResponse(long timeout) { + synchronized (mutex) { + while (!responseReceived) { + try { + mutex.wait(timeout); + } catch (InterruptedException e) { + return new ErrorResponse(-1,500, "Interrupted exception"); + } } + } return response; } - private synchronized void setResponse() { - responseReceived = true; - notify(); + private void setResponse(Response response) { + synchronized (mutex) { + this.response = response; + responseReceived = true; + mutex.notify(); + } } public Response sendAndReceive(String message, long timeout) { // Send request and wait response responseReceived = false; - send(message); - return waitResponse(timeout); + + try { + if (isOpen()) { + send(message); + return waitResponse(timeout); + } + } catch (WebsocketNotConnectedException e) { + return new ErrorResponse(-1,500, "WebsocketNotConnectedException"); + } + + return new ErrorResponse(-1,500, "Socket is closed"); + } @Override public void onOpen(ServerHandshake handshakedata) { - logger.debug("@onOpen"); + logger.debug("@onOpen STATE: " + getReadyState()); } @Override public void onClose(int code, String reason, boolean remote) { - logger.debug("@onClose code: " + code + " reason: " + reason + " remote: " + remote); - if (handler != null) handler.onBrokenConnection(); + logger.debug( + "@onClose code: " + code + " reason: " + reason + " remote: " + remote + " STATE: " + getReadyState()); + if (handler != null) + handler.onBrokenConnection(); } @Override public void onError(Exception ex) { - ex.printStackTrace(); - ErrorResponse error = new ErrorResponse(500, ex.getMessage()); - logger.debug("@onError: " + error); - if(handler!=null) handler.onError(error); + ErrorResponse error = new ErrorResponse(-1,500, "onError: "+ex.getMessage()); + + logger.debug("@onError: " + error + " STATE: " + getReadyState()); + + if (handler != null) + handler.onError(error); } @Override @@ -85,30 +108,24 @@ public void onMessage(String message) { JsonObject jsonMessage = null; try { jsonMessage = new JsonParser().parse(message).getAsJsonObject(); - } - catch(IllegalStateException e) { + } catch (IllegalStateException e) { logger.error(e.getMessage()); return; } - + if (jsonMessage.has("notification")) { JsonObject notification = jsonMessage.get("notification").getAsJsonObject(); if (notification.get("sequence").getAsInt() == 0) { - response = new SubscribeResponse(jsonMessage); - setResponse(); - } - else if(handler!=null) handler.onSemanticEvent(new Notification(jsonMessage)); - } - else if (jsonMessage.has("error")) { - response = new ErrorResponse(jsonMessage); - setResponse(); - if(handler!=null) handler.onError((ErrorResponse) response); - } - else if (jsonMessage.has("unsubscribed")) { - response = new UnsubscribeResponse(jsonMessage); - setResponse(); - } - else + setResponse(new SubscribeResponse(jsonMessage)); + } else if (handler != null) + handler.onSemanticEvent(new Notification(jsonMessage)); + } else if (jsonMessage.has("error")) { + setResponse(new ErrorResponse(jsonMessage)); + if (handler != null) + handler.onError((ErrorResponse) response); + } else if (jsonMessage.has("unsubscribed")) { + setResponse(new UnsubscribeResponse(jsonMessage)); + } else logger.error("Unknown message: " + message); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java index 7b15bc44..c0b79ff4 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java @@ -3,6 +3,7 @@ import java.io.IOException; import java.net.Socket; +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -21,22 +22,25 @@ public SPARQL11SESecureWebsocket(String wsUrl,SEPASecurityManager sm) } } - @Override - protected boolean connect() { - if (client == null) + public boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException { + if (handler == null) { + logger.fatal("Notification handler is null. Client cannot be initialized"); + throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); + } + + if (client == null) { + client = new SEPAWebsocketClient(wsURI, handler); + + // Enable secure socket + client.setSocket(secureSocket); + try { - client = new SEPAWebsocketClient(wsURI, handler); - // Enable secure socket - client.setSocket(secureSocket); - - if (!client.connectBlocking()) { - logger.error("Not connected"); - return false; - } + return client.connectBlocking(); } catch (InterruptedException e) { - logger.debug(e); - return false; + throw new SEPAProtocolException(e); } - return true; - } + } + + return client.isOpen(); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java index 3b6825d1..803dd2a5 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java @@ -20,164 +20,68 @@ public class SPARQL11SEWebsocket { protected SEPAWebsocketClient client = null; protected URI wsURI = null; - protected ISubscriptionHandler handler = null; public SPARQL11SEWebsocket(String wsUrl) throws SEPAProtocolException { - try { + try { wsURI = new URI(wsUrl); } catch (URISyntaxException e) { + logger.fatal(e.getMessage()); throw new SEPAProtocolException(e); } } - - public void setHandler(ISubscriptionHandler handler) throws SEPAProtocolException { + + public boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException { if (handler == null) { logger.fatal("Notification handler is null. Client cannot be initialized"); throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); - } - - this.handler = handler; - } - - public Response subscribe(SubscribeRequest req) { - if (req.getSPARQL() == null) - return new ErrorResponse(500, "SPARQL query is null"); - - try { - if (!connect()) { - return new ErrorResponse(500, "Failed to connect"); - } - } catch (SEPAProtocolException e1) { - return new ErrorResponse(500,e1.getMessage()); } - - if (!client.isOpen()) + + if (client == null) { + client = new SEPAWebsocketClient(wsURI, handler); + try { - client.reconnectBlocking(); + while(!client.connectBlocking()) { + Thread.sleep(100); + } } catch (InterruptedException e) { - e.printStackTrace(); - return new ErrorResponse(500, "Failed to re-connect"); + throw new SEPAProtocolException(e); } + } -// // Create SPARQL 1.1 Subscribe request -// JsonObject body = new JsonObject(); -// JsonObject request = new JsonObject(); -// body.add("sparql", new JsonPrimitive(req.getSPARQL() )); -// if (req.getAuthorizationHeader() != null) -// body.add("authorization", new JsonPrimitive(req.getAuthorizationHeader())); -// if (req.getAlias() != null) -// body.add("alias", new JsonPrimitive(req.getAlias())); -// request.add("subscribe", body); - - // Send request and wait for response - return client.sendAndReceive(req.toString(), TIMEOUT); + return client.isOpen(); } -// public Response subscribe(String sparql, String alias) { -// return _subscribe(sparql, null, alias); -// } -// -// public Response subscribe(String sparql) { -// return _subscribe(sparql, null, null); -// } + public Response subscribe(SubscribeRequest req) { + if (req.getSPARQL() == null) { + logger.error("SPARQL query is null"); + return new ErrorResponse(-1,500, "SPARQL query is null"); + } - protected boolean connect() throws SEPAProtocolException { - if (handler == null) { - logger.fatal("Notification handler is null. Client cannot be initialized"); - throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); - } - - if (client == null) - try { - client = new SEPAWebsocketClient(wsURI, handler); - if (!client.connectBlocking()) { - logger.error("Not connected"); - return false; - } - } catch (InterruptedException e) { - logger.debug(e); - return false; - } - return true; - } + if (client == null) { + logger.error("Client is null"); + return new ErrorResponse(-1,500, "Client is null"); + } -// protected Response _subscribe(String sparql, String authorization, String alias) { -// if (sparql == null) -// return new ErrorResponse(500, "SPARQL query is null"); -// -// try { -// if (!connect()) { -// return new ErrorResponse(500, "Failed to connect"); -// } -// } catch (SEPAProtocolException e1) { -// return new ErrorResponse(500,e1.getMessage()); -// } -// -// if (!client.isOpen()) -// try { -// client.reconnectBlocking(); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// return new ErrorResponse(500, "Failed to re-connect"); -// } -// -// // Create SPARQL 1.1 Subscribe request -// JsonObject body = new JsonObject(); -// JsonObject request = new JsonObject(); -// body.add("sparql", new JsonPrimitive(sparql)); -// if (authorization != null) -// body.add("authorization", new JsonPrimitive(authorization)); -// if (alias != null) -// body.add("alias", new JsonPrimitive(alias)); -// request.add("subscribe", body); -// -// // Send request and wait for response -// return client.sendAndReceive(request.toString(), TIMEOUT); -// } + // Send request and wait for response + return client.sendAndReceive(req.toString(), req.getTimeout()); + } public Response unsubscribe(UnsubscribeRequest req) { if (req.getSubscribeUUID() == null) - return new ErrorResponse(500, "SPUID is null"); - + return new ErrorResponse(-1,500, "SPUID is null"); + if (client == null) - return new ErrorResponse(500, "Client not connected"); - + return new ErrorResponse(-1,500, "Client is null"); + if (client.isClosed()) - return new ErrorResponse(500, "Socket is closed"); + return new ErrorResponse(-1,500, "Socket is closed"); // Send request and wait for response return client.sendAndReceive(req.toString(), TIMEOUT); } -// protected Response _unsubscribe(String spuid, String authorization) { -// if (spuid == null) -// return new ErrorResponse(500, "SPUID is null"); -// -// if (client == null) -// return new ErrorResponse(500, "Client not connected"); -// -// if (client.isClosed()) -// return new ErrorResponse(500, "Socket is closed"); -// -// // Create SPARQL 1.1 Unsubscribe request -// JsonObject request = new JsonObject(); -// JsonObject body = new JsonObject(); -// body.add("spuid", new JsonPrimitive(spuid)); -// if (authorization != null) -// body.add("authorization", new JsonPrimitive(authorization)); -// request.add("unsubscribe", body); -// -// // Send request and wait for response -// return client.sendAndReceive(request.toString(), TIMEOUT); -// } - public void close() { - if (isConnected()) { + if (client != null) client.close(); - } - } - - private boolean isConnected() { - return client != null && client.isOpen(); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java index 7dae0b0b..7dda1194 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java @@ -1,14 +1,11 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; -import org.apache.http.HttpStatus; - import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -24,57 +21,43 @@ public WebSocketSubscriptionProtocol(String host, int port, String path) throws wsClient = new SPARQL11SEWebsocket("ws://" + host + path); } - public WebSocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm) throws SEPAProtocolException, SEPASecurityException { + public WebSocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm) + throws SEPAProtocolException, SEPASecurityException { // WSS if (port != -1) wssClient = new SPARQL11SESecureWebsocket("wss://" + host + ":" + port + path, sm); else - wssClient = new SPARQL11SESecureWebsocket("wss://" + host + path,sm); + wssClient = new SPARQL11SESecureWebsocket("wss://" + host + path, sm); } @Override - public void setHandler(ISubscriptionHandler handler) throws SEPAProtocolException { - if (wsClient != null) - wsClient.setHandler(handler); - else if (wssClient != null) - wssClient.setHandler(handler); + public boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException { + if (!isSecure()) return wsClient.connect(handler); + else return wssClient.connect(handler); } @Override public void close() { - if (wsClient != null) + if (!isSecure()) wsClient.close(); - if (wssClient != null) + else wssClient.close(); } @Override public Response subscribe(SubscribeRequest request) { - if (request.getAuthorizationHeader() == null) { - if (wsClient == null) - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); - return wsClient.subscribe(request); - } else { - if (wssClient == null) - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); - - return wssClient.subscribe(request); - } + if (!isSecure()) return wsClient.subscribe(request); + + return wssClient.subscribe(request); } @Override public Response unsubscribe(UnsubscribeRequest request) { - if (request.getAuthorizationHeader() == null) { - if (wsClient == null) - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); + if (!isSecure()) return wsClient.unsubscribe(request); - } else { - if (wssClient == null) - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Client not initialized"); - + else return wssClient.unsubscribe(request); - } } @Override diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java index 328f6884..75dcdca6 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAProtocolException.java @@ -10,4 +10,8 @@ public class SEPAProtocolException extends Exception { public SEPAProtocolException(Throwable e){ super.initCause(e); } + + public SEPAProtocolException(String s) { + super.initCause(new Exception(s)); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index a89a5cce..352ac6a8 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -26,8 +26,6 @@ import java.net.URLEncoder; import java.net.UnknownHostException; import java.nio.charset.Charset; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import javax.net.ssl.SSLException; @@ -81,7 +79,8 @@ public class SPARQL11Protocol implements java.io.Closeable { protected SEPASecurityManager sm; public SPARQL11Protocol(SEPASecurityManager sm) { - if (sm == null) throw new IllegalArgumentException("Security manager is null"); + if (sm == null) + throw new IllegalArgumentException("Security manager is null"); this.sm = sm; httpClient = sm.getSSLHttpClient(); } @@ -93,7 +92,7 @@ public SPARQL11Protocol() { public boolean isSecure() { return sm != null; } - + private Response executeRequest(HttpUriRequest req, Request request) { CloseableHttpResponse httpResponse = null; HttpEntity responseEntity = null; @@ -108,7 +107,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { try { // Execute HTTP request - logger.trace(req.toString() + " (timeout: " + request.getTimeout() + " ms) "); + logger.trace(req.toString() + " " + request.toString() + " (timeout: " + request.getTimeout() + " ms) "); long start = Timings.getTime(); httpResponse = httpClient.execute(req); long stop = Timings.getTime(); @@ -220,38 +219,37 @@ private Response executeRequest(HttpUriRequest req, Request request) { */ public Response update(UpdateRequest req) { switch (req.getHttpMethod()) { - case GET: - // *********************** - // OpenLink VIRTUOSO PATCH (not supported by SPARQL 1.1 Protocol) - // *********************** - return patchVirtuoso(req); case POST: return post(req); case URL_ENCODED_POST: return post(req); + default: + return new ErrorResponse(req.getToken(), HttpStatus.SC_BAD_REQUEST, + "SPARQL 1.1 Update supports POST method only"); } - - return post(req); } + /** + * SPARQL 1.1 Protocol + * + * * + * + *
    +	 *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    +	 *----------------------------------------------------------------------------------------------------------------------------------------
    +	 * update via URL-encoded POST|   POST         None                              application/x-www-form-urlencoded   URL-encoded, ampersand-separated query parameters.
    +	 *                            |                                                                                     update (exactly 1)
    +	 *                            |                                                                                     using-graph-uri (0 or more)
    +	 *                            |                                                                                     using-named-graph-uri (0 or more)
    +	 *----------------------------------------------------------------------------------------------------------------------------------------																													
    +	 * update via POST directly   |    POST       using-graph-uri (0 or more)       application/sparql-update           Unencoded SPARQL update request string
    +	 *                                            using-named-graph-uri (0 or more)
    +	 * 
    + */ private Response post(UpdateRequest req) { StringEntity requestEntity = null; HttpPost post; - String graphs = ""; - - try { - if (req.getUsingGraphUri() != null) { - graphs += "using-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); - if (req.getUsingNamedGraphUri() != null) { - graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); - } - } else if (req.getUsingNamedGraphUri() != null) { - graphs += "using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } + String graphs = null; // Setting URL String scheme = req.getScheme(); @@ -259,120 +257,165 @@ private Response post(UpdateRequest req) { int port = req.getPort(); String updatePath = req.getPath(); + // Create POST request try { if (req.getHttpMethod().equals(HTTPMethod.POST)) { + if (req.getUsingGraphUri() != null) { + graphs = "using-graph-uri=" + req.getUsingGraphUri(); + if (req.getUsingNamedGraphUri() != null) { + graphs += "&using-named-graph-uri=" + req.getUsingNamedGraphUri(); + } + } else if (req.getUsingNamedGraphUri() != null) { + graphs = "using-named-graph-uri=" + req.getUsingNamedGraphUri(); + } + post = new HttpPost(new URI(scheme, null, host, port, updatePath, graphs, null)); post.setHeader("Content-Type", "application/sparql-update"); + + // Body + requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); } else { post = new HttpPost(new URI(scheme, null, host, port, updatePath, null, null)); post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + // Graphs + try { + if (req.getUsingGraphUri() != null) { + graphs = "using-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); + if (req.getUsingNamedGraphUri() != null) { + graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } + } else if (req.getUsingNamedGraphUri() != null) { + graphs = "using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + // Body + if (graphs != null) requestEntity = new StringEntity("update=" + URLEncoder.encode(req.getSPARQL(), "UTF-8") + "&" + graphs, + Consts.UTF_8); + else requestEntity = new StringEntity("update=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"), + Consts.UTF_8); } - } catch (URISyntaxException e) { + } catch (URISyntaxException | UnsupportedEncodingException e) { logger.error(e.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } + // Accept header post.setHeader("Accept", req.getAcceptHeader()); - requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); + // Body post.setEntity(requestEntity); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) - .setConnectTimeout(req.getTimeout()).build(); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout((int) req.getTimeout()) + .setConnectTimeout((int) req.getTimeout()).build(); post.setConfig(requestConfig); return executeRequest(post, req); } - private Response patchVirtuoso(UpdateRequest req) { - // 1) "INSERT DATA" is not supported. Only INSERT (also if the WHERE is not - // present). - String fixedSparql = req.getSPARQL(); - Pattern p = null; - try { - p = Pattern.compile( - "(?.*)(delete)([^{]*)(?.*)(insert)([^{]*)(?.*)|(?.*)(delete)(?[^{]*)(?.*)|(?.*)(insert)([^{]*)(?.*)", - Pattern.CASE_INSENSITIVE); - - Matcher m = p.matcher(req.getSPARQL()); - if (m.matches()) { - if (m.group("update") != null) { - fixedSparql = m.group("update") + " DELETE " + m.group("udtriples") + " INSERT " - + m.group("uitriples"); - } else if (m.group("insert") != null) { - fixedSparql = m.group("insert") + " INSERT " + m.group("itriples"); - } else { - if (m.group("where") != null) { - if (m.group("where").toLowerCase().contains("where")) { - fixedSparql = m.group("delete") + " DELETE " + m.group("where") + m.group("dtriples"); - } - else - fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples"); - } - else fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples"); - } - } - } catch (Exception e) { - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - - // 2) SPARQL 1.1 Update are issued as GET request (like for a SPARQL 1.1 Query) - String query; - try { - // custom "format" parameter - query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format=" - + URLEncoder.encode(req.getAcceptHeader(), "UTF-8"); - } catch (UnsupportedEncodingException e1) { - logger.error(e1.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); - } - - // 3) Named-graphs specified like a query - String graphs = ""; - try { - if (req.getUsingGraphUri() != null) { - - graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); - - if (req.getUsingNamedGraphUri() != null) { - graphs += "&named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); - } - } else if (req.getUsingNamedGraphUri() != null) { - graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } - - if (!graphs.equals("")) - query += "&" + graphs; - - logger.debug("Query: " + query); - - // Setting URL - String scheme = req.getScheme(); - String host = req.getHost(); - int port = req.getPort(); - String queryPath = req.getPath(); - - String url; - if (port != -1) - url = scheme + "://" + host + ":" + port + queryPath + "?" + query; - else - url = scheme + "://" + host + queryPath + "?" + query; - - HttpGet get; - get = new HttpGet(url); - - get.setHeader("Accept", req.getAcceptHeader()); - - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) - .setConnectTimeout(req.getTimeout()).build(); - get.setConfig(requestConfig); - - return executeRequest(get, req); - } + // private Response patchVirtuoso(UpdateRequest req) { + // // 1) "INSERT DATA" is not supported. Only INSERT (also if the WHERE is not + // // present). + // String fixedSparql = req.getSPARQL(); + // Pattern p = null; + // try { + // p = Pattern.compile( + // "(?.*)(delete)([^{]*)(?.*)(insert)([^{]*)(?.*)|(?.*)(delete)(?[^{]*)(?.*)|(?.*)(insert)([^{]*)(?.*)", + // Pattern.CASE_INSENSITIVE); + // + // Matcher m = p.matcher(req.getSPARQL()); + // if (m.matches()) { + // if (m.group("update") != null) { + // fixedSparql = m.group("update") + " DELETE " + m.group("udtriples") + " + // INSERT " + // + m.group("uitriples"); + // } else if (m.group("insert") != null) { + // fixedSparql = m.group("insert") + " INSERT " + m.group("itriples"); + // } else { + // if (m.group("where") != null) { + // if (m.group("where").toLowerCase().contains("where")) { + // fixedSparql = m.group("delete") + " DELETE " + m.group("where") + + // m.group("dtriples"); + // } + // else + // fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples"); + // } + // else fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples"); + // } + // } + // } catch (Exception e) { + // return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, + // e.getMessage()); + // } + // + // // 2) SPARQL 1.1 Update are issued as GET request (like for a SPARQL 1.1 + // Query) + // String query; + // try { + // // custom "format" parameter + // query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format=" + // + URLEncoder.encode(req.getAcceptHeader(), "UTF-8"); + // } catch (UnsupportedEncodingException e1) { + // logger.error(e1.getMessage()); + // return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, + // e1.getMessage()); + // } + // + // // 3) Named-graphs specified like a query + // String graphs = ""; + // try { + // if (req.getUsingGraphUri() != null) { + // + // graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), + // "UTF-8"); + // + // if (req.getUsingNamedGraphUri() != null) { + // graphs += "&named-graph-uri=" + + // URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + // } + // } else if (req.getUsingNamedGraphUri() != null) { + // graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), + // "UTF-8"); + // } + // } catch (UnsupportedEncodingException e) { + // logger.error(e.getMessage()); + // return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, + // e.getMessage()); + // } + // + // if (!graphs.equals("")) + // query += "&" + graphs; + // + // logger.debug("Query: " + query); + // + // // Setting URL + // String scheme = req.getScheme(); + // String host = req.getHost(); + // int port = req.getPort(); + // String queryPath = req.getPath(); + // + // String url; + // if (port != -1) + // url = scheme + "://" + host + ":" + port + queryPath + "?" + query; + // else + // url = scheme + "://" + host + queryPath + "?" + query; + // + // HttpGet get; + // get = new HttpGet(url); + // + // get.setHeader("Accept", req.getAcceptHeader()); + // + // RequestConfig requestConfig = + // RequestConfig.custom().setSocketTimeout(req.getTimeout()) + // .setConnectTimeout(req.getTimeout()).build(); + // get.setConfig(requestConfig); + // + // return executeRequest(get, req); + // } /** * Implements a SPARQL 1.1 query operation @@ -466,27 +509,26 @@ public Response query(QueryRequest req) { return post(req); } + /** + *
    +	 *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    +	 *----------------------------------------------------------------------------------------------------------------------------------------											
    +	 * query via URL-encoded POST |   POST         None                              application/x-www-form-urlencoded   URL-encoded, ampersand-separated query parameters.
    +	 *                            |                                                                                     query (exactly 1)
    +	 *                            |                                                                                     default-graph-uri (0 or more)
    +	 *                            |                                                                                     named-graph-uri (0 or more)
    +	 *----------------------------------------------------------------------------------------------------------------------------------------																													
    +	 * query via POST directly    |   POST         default-graph-uri (0 or more)
    +	 *                            |                named-graph-uri (0 or more)       application/sparql-query            Unencoded SPARQL query string
    +	 *
    +	 * 
    + */ private Response post(QueryRequest req) { StringEntity requestEntity = null; HttpPost post; // Graphs - String graphs = ""; - try { - if (req.getDefaultGraphUri() != null) { - - graphs += "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); - - if (req.getNamedGraphUri() != null) { - graphs += "&named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); - } - } else if (req.getNamedGraphUri() != null) { - graphs += "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); - } - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); - } + String graphs = null; // Setting URL String scheme = req.getScheme(); @@ -496,29 +538,73 @@ private Response post(QueryRequest req) { try { if (req.getHttpMethod().equals(HTTPMethod.POST)) { + if (req.getDefaultGraphUri() != null) { + + graphs = "default-graph-uri=" + req.getDefaultGraphUri(); + + if (req.getNamedGraphUri() != null) { + graphs += "&named-graph-uri=" + req.getNamedGraphUri(); + } + } else if (req.getNamedGraphUri() != null) { + graphs = "named-graph-uri=" + req.getNamedGraphUri(); + } + post = new HttpPost(new URI(scheme, null, host, port, queryPath, graphs, null)); post.setHeader("Content-Type", "application/sparql-query"); + + // Body + requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); } else { post = new HttpPost(new URI(scheme, null, host, port, queryPath, null, null)); post.setHeader("Content-Type", "application/x-www-form-urlencoded"); + + try { + if (req.getDefaultGraphUri() != null) { + + graphs = "default-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); + + if (req.getNamedGraphUri() != null) { + graphs += "&named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + } + } else if (req.getNamedGraphUri() != null) { + graphs = "named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); + } + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } + + // Body + if (graphs != null) requestEntity = new StringEntity("query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8") + "&" + graphs, + Consts.UTF_8); + else requestEntity = new StringEntity("query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"), + Consts.UTF_8); } - } catch (URISyntaxException e) { + } catch (URISyntaxException | UnsupportedEncodingException e) { logger.error(e.getMessage()); return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } post.setHeader("Accept", req.getAcceptHeader()); - requestEntity = new StringEntity(req.getSPARQL(), Consts.UTF_8); post.setEntity(requestEntity); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) - .setConnectTimeout(req.getTimeout()).build(); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout((int) req.getTimeout()) + .setConnectTimeout((int) req.getTimeout()).build(); post.setConfig(requestConfig); return executeRequest(post, req); } + /** + *
    +	 *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    +	 *----------------------------------------------------------------------------------------------------------------------------------------
    +	 * query via GET              |   GET          query (exactly 1)                 None                                None
    +	 *                            |                default-graph-uri (0 or more)
    +	 *                            |                named-graph-uri (0 or more)
    +	 * 
    + */ private Response get(QueryRequest req) { String query; try { @@ -565,8 +651,8 @@ private Response get(QueryRequest req) { get.setHeader("Accept", req.getAcceptHeader()); - RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(req.getTimeout()) - .setConnectTimeout(req.getTimeout()).build(); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout((int) req.getTimeout()) + .setConnectTimeout((int) req.getTimeout()).build(); get.setConfig(requestConfig); return executeRequest(get, req); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index 3da6c929..1fd5e555 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -18,9 +18,6 @@ package it.unibo.arces.wot.sepa.commons.request; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; - import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; /** @@ -92,21 +89,11 @@ public String getAcceptHeader() { } public String getDefaultGraphUri() { - if (default_graph_uri == null) return null; - try { - return URLDecoder.decode(default_graph_uri,"UTF-8"); - } catch (UnsupportedEncodingException e) { - return null; - } + return default_graph_uri; } public String getNamedGraphUri() { - if (named_graph_uri == null) return null; - try { - return URLDecoder.decode(named_graph_uri,"UTF-8"); - } catch (UnsupportedEncodingException e) { - return null; - } + return named_graph_uri; } /** diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java index a84550ef..a0f13fe3 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java @@ -49,7 +49,7 @@ public abstract class Request { protected HTTPMethod method = HTTPMethod.POST; protected String id = null; - protected int timeout = 5000; + protected long timeout = 5000; protected String scheme = null; protected String host = null; @@ -145,7 +145,7 @@ public String getID() { return id; } - public int getTimeout() { + public long getTimeout() { return timeout; } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java index 2081b036..1b9e9e66 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java @@ -18,9 +18,6 @@ package it.unibo.arces.wot.sepa.commons.request; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; - import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.request.Request; @@ -56,7 +53,7 @@ public UpdateRequest(String sparql) { super(sparql); } - public UpdateRequest(Integer token,HTTPMethod method,String scheme,String host, int port, String path,String sparql,int timeout,String using_graph_uri,String using_named_graph_uri,String authorization) { + public UpdateRequest(Integer token,HTTPMethod method,String scheme,String host, int port, String path,String sparql,long timeout,String using_graph_uri,String using_named_graph_uri,String authorization) { super(token,sparql); super.method = method; @@ -104,21 +101,11 @@ public String toString() { */ public String getUsingGraphUri() { - if (using_graph_uri == null) return null; - try { - return URLDecoder.decode(using_graph_uri,"UTF-8"); - } catch (UnsupportedEncodingException e) { - return null; - } + return using_graph_uri; } public String getUsingNamedGraphUri() { - if (using_named_graph_uri == null) return null; - try { - return URLDecoder.decode(using_named_graph_uri,"UTF-8"); - } catch (UnsupportedEncodingException e) { - return null; - } + return using_named_graph_uri; } public String getAcceptHeader() { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java index 4e1f5987..5f2bff1c 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java @@ -23,7 +23,6 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -// TODO: Auto-generated Javadoc /** * This class represents the response to a SPARQL 1.1 Subscribe (see SPARQL 1.1 * Subscription Language) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java index 603cc6e3..0ec10bf9 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java @@ -323,7 +323,7 @@ public Response register(String url, String identity) { String id = json.get("credentials").getAsJsonObject().get("client_id").getAsString(); String secret = json.get("credentials").getAsJsonObject().get("client_secret").getAsString(); JsonElement signature = json.get("credentials").getAsJsonObject().get("signature"); - Timings.log("REGISTER", start, Timings.getTime()); + Timings.log("REGISTER", start,Timings.getTime()); return new RegistrationResponse(id, secret, signature); } catch (Exception e) { diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java index 3400f848..1469b6e8 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java @@ -15,7 +15,7 @@ public static long getTime() { } public synchronized static void log(String tag,long start,long stop) { - String message = String.format("%d,%s,%d",start,tag,stop-start); + String message = String.format("%d,%s,%d",System.currentTimeMillis(),tag,stop-start); logger.log(Level.getLevel("timing"),message); } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index 4ffbe87e..03a8ffa9 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -14,7 +14,7 @@ public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEP if( configuaration != null){ result = new JSAP(configuaration); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("chat.jsap"); + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); result = new JSAP(config.getPath()); } return result; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java deleted file mode 100644 index c1eaa96e..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITProtocolTest.java +++ /dev/null @@ -1,154 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.io.IOException; -import java.util.HashMap; - -import static org.junit.Assert.*; - -public class ITProtocolTest { - - protected static HashMap updates = new HashMap(); - protected static HashMap queries = new HashMap(); - protected static HashMap subscribes = new HashMap(); - - protected static HashMap protocols = new HashMap(); - - protected static JSAP properties = null; - - protected static final MockSubscriptionHandler subHandler = new MockSubscriptionHandler();; - - protected static SPARQL11Protocol client = null; - protected static SPARQL11SEProtocol seClient = null; - - @BeforeClass - public static void init() throws Exception { - properties = ConfigurationProvider.GetTestEnvConfiguration(); - - for (String id : properties.getQueryIds()) { - queries.put(id, getQueryRequest(id, 5000, null)); - subscribes.put(id, getSubscribeRequest(id, null)); - protocols.put(id, new WebSocketSubscriptionProtocol(properties.getSubscribeHost(id), - properties.getSubscribePort(id), properties.getSubscribePath(id))); - } - - for (String id : properties.getUpdateIds()) { - updates.put(id, getUpdateRequest(id, 5000, null)); - } - } - - @AfterClass - public static void dispose() throws IOException { - - } - - @Before - public void beginTest() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { - client = new SPARQL11Protocol(); - seClient = new SPARQL11SEProtocol(protocols.get("Q2"), subHandler); - - final Response ret = client.update(updates.get("DELETE_ALL")); - - assertFalse(String.valueOf(ret), ret.isError()); - } - - @After - public void endTest() throws IOException { - if (client != null) client.close(); - if (seClient != null) seClient.close(); - } - - @Test(timeout = 5000) - public void Update() throws IOException { - final Response ret = client.update(updates.get("U1")); - assertFalse(String.valueOf(ret), ret.isError()); - } - - @Test(timeout = 5000) - public void Query() throws IOException { - final Response ret = client.query(queries.get("Q1")); - assertFalse(String.valueOf(ret), ret.isError()); - } - - @Test(timeout = 5000) - public void UpdateAndQueryOneResult() throws IOException { - final Response updateRes = client.update(updates.get("U2")); - final Response queryRes = client.query(queries.get("Q2")); - - if (updateRes.isError()) assertFalse("Update: " + String.valueOf(updateRes), true); - if (queryRes.isError()) assertFalse(" Query: " + String.valueOf(queryRes), true); - - assertFalse(String.valueOf(queryRes), ((QueryResponse) queryRes).getBindingsResults().size() != 1); - } - - @Test(timeout = 5000) - public void SubscribeAndNotifyOneAddedResult() - throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException { - - final Response subRes = seClient.subscribe(subscribes.get("Q2")); - final Response updateRes = seClient.update(updates.get("U2")); - - Response notify = subHandler.getResponse(); - - if (subRes.isError()) assertFalse(" Subscribe: " + String.valueOf(subRes), true); - if (updateRes.isError()) assertFalse("Update: " + String.valueOf(updateRes), true); - if (notify.isError()) assertFalse(String.valueOf(notify), true); - - if (((Notification)notify).getARBindingsResults().getAddedBindings().size() != 1) assertFalse(String.valueOf(notify), true); - } - - protected static UpdateRequest getUpdateRequest(String id, int timeout, String authorization) { - HTTPMethod method = properties.getUpdateMethod(id); - String scheme = properties.getUpdateProtocolScheme(id); - String host = properties.getUpdateHost(id); - int port = properties.getUpdatePort(id); - String path = properties.getUpdatePath(id); - String sparql = properties.getSPARQLUpdate(id); - String graphUri = properties.getUsingGraphURI(id); - String namedGraphUri = properties.getUsingNamedGraphURI(id); - - return new UpdateRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, - authorization); - } - - protected static QueryRequest getQueryRequest(String id, int timeout, String authorization) { - HTTPMethod method = properties.getQueryMethod(id); - String scheme = properties.getQueryProtocolScheme(id); - String host = properties.getQueryHost(id); - int port = properties.getQueryPort(id); - String path = properties.getQueryPath(id); - String sparql = properties.getSPARQLQuery(id); - String graphUri = properties.getDefaultGraphURI(id); - String namedGraphUri = properties.getNamedGraphURI(id); - - return new QueryRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, - authorization); - } - - protected static SubscribeRequest getSubscribeRequest(String id, String authorization) { - String sparql = properties.getSPARQLQuery(id); - String graphUri = properties.getDefaultGraphURI(id); - String namedGraphUri = properties.getNamedGraphURI(id); - - return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization); - } -} \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java new file mode 100644 index 00000000..bd1775f6 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -0,0 +1,255 @@ +package it.unibo.arces.wot.sepa.api; + +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; +import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.QueryResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.commons.sparql.Bindings; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.*; + +public class ITSPARQL11SEProtocol { + protected static JSAP properties = null; + private static SEPASecurityManager sm; + private static SPARQL11SEProtocol client = null; + + private final static String VALID_ID = "SEPATest"; + private final static String NOT_VALID_ID = "RegisterMePlease"; + + private static AtomicLong mutex = new AtomicLong(0); + + @BeforeClass + public static void init() throws Exception { + properties = ConfigurationProvider.GetTestEnvConfiguration(); + + if (properties.isSecure()) { + sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", + new AuthenticationProperties(properties.getFileName())); + + // Registration + Response response = sm.register(NOT_VALID_ID); + assertFalse("Failed to register a not valid ID", !response.isError()); + response = sm.register(VALID_ID); + assertFalse("Failed to register a valid ID", response.isError()); + } + } + + @AfterClass + public static void dispose() throws IOException { + + } + + @Before + public void beginTest() throws IOException, IllegalArgumentException, SEPAProtocolException, + SEPAPropertiesException, SEPASecurityException { + + ISubscriptionProtocol protocol = new WebSocketSubscriptionProtocol(properties.getDefaultHost(),properties.getSubscribePort(),properties.getSubscribePath()); + + if (sm == null) + client = new SPARQL11SEProtocol(protocol, new MockSubscriptionHandler()); + else + client = new SPARQL11SEProtocol(protocol, new MockSubscriptionHandler(), sm); + + assertFalse("Failed to create SPARQL11SEProtocol", client == null); + + // Delete all triples + Response ret = client.update(buildUpdateRequest("DELETE_ALL",5000)); + assertFalse(String.valueOf(ret), ret.isError()); + + // Check that the store size is 0 + ret = client.query(buildQueryRequest("COUNT",5000)); + assertFalse(String.valueOf(ret), ret.isError()); + QueryResponse results = (QueryResponse) ret; + assertFalse(String.valueOf(results), results.getBindingsResults().size() != 1); + for (Bindings bindings : results.getBindingsResults().getBindings()) { + assertFalse(String.valueOf(results), bindings.getValue("n") == null); + assertTrue(String.valueOf(results), bindings.getValue("n").equals("0")); + } + } + + @After + public void endTest() throws IOException { + if (client != null) + client.close(); + } + + @Test(timeout = 5000) + public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException { + Response ret = client.update(buildUpdateRequest("VAIMEE",5000)); + assertFalse(String.valueOf(ret), ret.isError()); + } + + @Test(timeout = 5000) + public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException { + Response ret = client.query(buildQueryRequest("ALL",5000)); + assertFalse(String.valueOf(ret), ret.isError()); + } + + @Test(timeout = 5000) + public void UpdateAndQuery() throws IOException, SEPAPropertiesException, SEPASecurityException { + Response ret = client.update(buildUpdateRequest("VAIMEE",5000)); + assertFalse(String.valueOf(ret), ret.isError()); + + ret = client.query(buildQueryRequest("ALL",5000)); + assertFalse(String.valueOf(ret), ret.isError()); + assertFalse(String.valueOf(ret), ((QueryResponse) ret).getBindingsResults().size() != 1); + } + + @Test(timeout = 5000) + public void Subscribe() throws SEPAPropertiesException, SEPASecurityException { + Response ret = client.subscribe(buildSubscribeRequest("ALL")); + assertFalse(String.valueOf(ret), ret.isError()); + } + + @Test(timeout = 5000) + public void Subscribex10() throws SEPAPropertiesException, SEPASecurityException { + for (int i= 0; i < 10 ; i++) { + Response ret = client.subscribe(buildSubscribeRequest("ALL")); + assertFalse("Failed at: "+i+" "+String.valueOf(ret), ret.isError()); + } + } + + @Test(timeout = 5000) + public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException { + Response ret = client.subscribe(buildSubscribeRequest("ALL")); + assertFalse(String.valueOf(ret), ret.isError()); + + ret = client.unsubscribe(buildUnsubscribeRequest(((SubscribeResponse)ret).getSpuid())); + assertFalse(String.valueOf(ret), ret.isError()); + } + + @Test(timeout = 5000) + public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, + SEPAPropertiesException, SEPASecurityException { + + new Subscriber("ALL",properties,sm,mutex).start(); + + Thread.sleep(1000); + + Update(); + + synchronized (mutex) { + mutex.wait(); + } + } + + @Test(timeout = 5000) + public void NotifyNxM() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, + SEPAPropertiesException, SEPASecurityException { + int n = 10; + + mutex.set(n*n*n); + + for (int i=0; i < n; i++) new Subscriber("ALL",properties,sm,mutex).start(); + + Thread.sleep(1000); + + for (int i=0; i < n; i++) new Publisher("RANDOM",properties,sm,n).start(); + + synchronized (mutex) { + while(mutex.get() > 0) mutex.wait(); + } + } + + @Test(timeout = 60000) + public void Notify2Nx3M() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, + SEPAPropertiesException, SEPASecurityException { + int n = 10; + int updates = n*n*n; + + int nAll = updates * n; + int nRandom = n * n * n; + int nRandom1 = n * n * n; + + mutex.set(nAll+nRandom+nRandom1); + + for (int i=0; i < n; i++) { + new Subscriber("ALL",properties,sm,mutex).start(); + new Subscriber("RANDOM",properties,sm,mutex).start(); + new Subscriber("RANDOM1",properties,sm,mutex).start(); + } + + Thread.sleep(1000); + + for (int i=0; i < n; i++) { + new Publisher("RANDOM",properties,sm,n).start(); + new Publisher("RANDOM1",properties,sm,n).start(); + } + + synchronized (mutex) { + while(mutex.get() > 0) mutex.wait(); + } + } + + protected static UpdateRequest buildUpdateRequest(String id, int timeout) throws SEPAPropertiesException, SEPASecurityException { + HTTPMethod method = properties.getUpdateMethod(id); + String scheme = properties.getUpdateProtocolScheme(id); + String host = properties.getUpdateHost(id); + int port = properties.getUpdatePort(id); + String path = properties.getUpdatePath(id); + String sparql = properties.getSPARQLUpdate(id); + String graphUri = properties.getUsingGraphURI(id); + String namedGraphUri = properties.getUsingNamedGraphURI(id); + + String authorization = null; + if (sm != null) authorization = sm.getAuthorizationHeader(); + + return new UpdateRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, + authorization); + } + + protected static QueryRequest buildQueryRequest(String id, int timeout) throws SEPAPropertiesException, SEPASecurityException { + HTTPMethod method = properties.getQueryMethod(id); + String scheme = properties.getQueryProtocolScheme(id); + String host = properties.getQueryHost(id); + int port = properties.getQueryPort(id); + String path = properties.getQueryPath(id); + String sparql = properties.getSPARQLQuery(id); + String graphUri = properties.getDefaultGraphURI(id); + String namedGraphUri = properties.getNamedGraphURI(id); + + String authorization = null; + if (sm != null) authorization = sm.getAuthorizationHeader(); + + return new QueryRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, + authorization); + } + + protected static SubscribeRequest buildSubscribeRequest(String id) throws SEPAPropertiesException, SEPASecurityException { + String sparql = properties.getSPARQLQuery(id); + String graphUri = properties.getDefaultGraphURI(id); + String namedGraphUri = properties.getNamedGraphURI(id); + + String authorization = null; + if (sm != null) authorization = sm.getAuthorizationHeader(); + + return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization); + } + + private UnsubscribeRequest buildUnsubscribeRequest(String spuid) throws SEPAPropertiesException, SEPASecurityException { + String authorization = null; + if (sm != null) authorization = sm.getAuthorizationHeader(); + + return new UnsubscribeRequest(spuid, authorization); + } +} \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java deleted file mode 100644 index 07c60c63..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSecureProtocolTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -import org.junit.Before; -import org.junit.BeforeClass; - -import java.io.IOException; -import static org.junit.Assert.*; - -public class ITSecureProtocolTest extends ITProtocolTest { - private final static String VALID_ID = "SEPATest"; - private final static String NOT_VALID_ID = "RegisterMePlease"; - - private static SEPASecurityManager sm; - - @BeforeClass - public static void init() throws Exception { - properties = ConfigurationProvider.GetTestEnvConfiguration(); - - sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", - new AuthenticationProperties(properties.getFileName())); - - // Registration - Response response = sm.register(NOT_VALID_ID); - assertFalse("Failed to register a not valid ID", !response.isError()); - response = sm.register(VALID_ID); - assertFalse("Failed to register a valid ID", response.isError()); - - for (String id : properties.getQueryIds()) { - queries.put(id, getQueryRequest(id, 5000, sm.getAuthorizationHeader())); - subscribes.put(id, getSubscribeRequest(id, sm.getAuthorizationHeader())); - protocols.put(id, new WebSocketSubscriptionProtocol(properties.getSubscribeHost(id), - properties.getSubscribePort(id), properties.getSubscribePath(id),sm)); - } - - for (String id : properties.getUpdateIds()) { - updates.put(id, getUpdateRequest(id, 5000, sm.getAuthorizationHeader())); - } - } - - @Before - public void beginTest() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { - client = new SPARQL11Protocol(sm); - seClient = new SPARQL11SEProtocol(protocols.get("Q2"), subHandler,sm); - - // Set authorization header - for (UpdateRequest req : updates.values()) req.setAuthorizationHeader(sm.getAuthorizationHeader()); - for (QueryRequest req : queries.values()) req.setAuthorizationHeader(sm.getAuthorizationHeader()); - for (SubscribeRequest req : subscribes.values()) req.setAuthorizationHeader(sm.getAuthorizationHeader()); - - final Response ret = client.update(updates.get("DELETE_ALL")); - - assertFalse(String.valueOf(ret), ret.isError()); - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java new file mode 100644 index 00000000..b65ecf03 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -0,0 +1,65 @@ +package it.unibo.arces.wot.sepa.api; + +import java.util.concurrent.atomic.AtomicLong; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +public class Publisher extends Thread { + private JSAP properties; + private SEPASecurityManager sm; + private SPARQL11Protocol client = null; + private String id; + + private AtomicLong running; + + public Publisher(String id,JSAP properties, SEPASecurityManager sm,long n) { + this.properties = properties; + this.sm = sm; + this.id = id; + + if (sm != null) + client = new SPARQL11Protocol(sm); + else + client = new SPARQL11Protocol(); + + running = new AtomicLong(n); + } + + public void run() { + while(running.get() > 0) { + try { + client.update(buildUpdateRequest(id,5000)); + } catch (SEPAPropertiesException | SEPASecurityException e) { + running.set(running.get()-1); + } + } + } + + public void interrupt() { + super.interrupt(); + running.set(0); + } + + protected UpdateRequest buildUpdateRequest(String id, int timeout) throws SEPAPropertiesException, SEPASecurityException { + HTTPMethod method = properties.getUpdateMethod(id); + String scheme = properties.getUpdateProtocolScheme(id); + String host = properties.getUpdateHost(id); + int port = properties.getUpdatePort(id); + String path = properties.getUpdatePath(id); + String sparql = properties.getSPARQLUpdate(id); + String graphUri = properties.getUsingGraphURI(id); + String namedGraphUri = properties.getUsingNamedGraphURI(id); + + String authorization = null; + if (sm != null) authorization = sm.getAuthorizationHeader(); + + return new UpdateRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, + authorization); + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java new file mode 100644 index 00000000..4e977343 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -0,0 +1,122 @@ +package it.unibo.arces.wot.sepa.api; + +import static org.junit.Assert.assertFalse; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +public class Subscriber extends Thread implements ISubscriptionHandler { + + private JSAP properties; + private SEPASecurityManager sm; + private AtomicLong mutex; + private SPARQL11SEProtocol client = null; + private ISubscriptionProtocol protocol = null; + private String id; + + private AtomicBoolean running = new AtomicBoolean(true); + + public Subscriber(String id,JSAP properties, SEPASecurityManager sm,AtomicLong mutex) { + this.properties = properties; + this.sm = sm; + this.mutex = mutex; + this.id = id; + + try { + protocol = new WebSocketSubscriptionProtocol(properties.getDefaultHost(),properties.getSubscribePort(),properties.getSubscribePath()); + } catch (SEPAProtocolException e2) { + assertFalse(e2.getMessage(),true); + } + + if (sm != null) + try { + client = new SPARQL11SEProtocol(protocol, this, sm); + } catch (SEPAProtocolException e1) { + assertFalse(e1.getMessage(), true); + } + else + try { + client = new SPARQL11SEProtocol(protocol, this); + } catch (IllegalArgumentException | SEPAProtocolException e1) { + assertFalse(e1.getMessage(), true); + } + } + + public void run() { + Response ret = null; + try { + ret = client.subscribe(buildSubscribeRequest(id)); + } catch (SEPAPropertiesException | SEPASecurityException e1) { + assertFalse(e1.getMessage(), true); + } + assertFalse(String.valueOf(ret), ret.isError()); + + while(running.get()) { + synchronized(running) { + try { + running.wait(); + } catch (InterruptedException e) { + running.set(false); + } + } + } + + try { + client.close(); + } catch (IOException e) { + assertFalse(e.getMessage(), true); + } + } + + public void interrupt() { + super.interrupt(); + + synchronized(running) { + running.set(true); + running.notify(); + } + } + + private SubscribeRequest buildSubscribeRequest(String id) throws SEPAPropertiesException, SEPASecurityException { + String sparql = properties.getSPARQLQuery(id); + String graphUri = properties.getDefaultGraphURI(id); + String namedGraphUri = properties.getNamedGraphURI(id); + + String authorization = null; + if (sm != null) authorization = sm.getAuthorizationHeader(); + + return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization); + } + + @Override + public void onSemanticEvent(Notification notify) { + synchronized (mutex) { + mutex.set(mutex.get()-1); + mutex.notify(); + } + } + + @Override + public void onBrokenConnection() { + // TODO Auto-generated method stub + + } + + @Override + public void onError(ErrorResponse errorResponse) { + // TODO Auto-generated method stub + + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java new file mode 100644 index 00000000..0daf9d63 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java @@ -0,0 +1,118 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.atomic.AtomicLong; + +import org.junit.BeforeClass; +import org.junit.Test; + +import it.unibo.arces.wot.sepa.api.MockSubscriptionHandler; +import it.unibo.arces.wot.sepa.api.protocol.websocket.SEPAWebsocketClient; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.response.Response; + +import static org.junit.Assert.*; + +public class ITSEPAWebsocketClient { + private static int N_THREADS = 100; + private static AtomicLong mutex = new AtomicLong(N_THREADS); + + @BeforeClass + public static void init() throws SEPAPropertiesException, SEPASecurityException { + + } + + @Test(timeout = 10000) + public void Subscribe() { + mutex = new AtomicLong(N_THREADS); + + for (int i = 0; i < N_THREADS; i++) { + new Thread() { + public void run() { + URI ws = null; + try { + ws = new URI("ws://localhost:9000/subscribe"); + } catch (URISyntaxException e) { + assertFalse(e.getMessage(), true); + } + MockSubscriptionHandler handler = new MockSubscriptionHandler(); + + SEPAWebsocketClient client = new SEPAWebsocketClient(ws, handler); + try { + client.connectBlocking(); + } catch (InterruptedException e) { + assertFalse(e.getMessage(), true); + } + if(client.isOpen()) { + client.send("{\"subscribe\":{\"sparql\":\"select * where {?x ?y ?z}\",\"default-graph-uri\":\"http://sepatest\"}}"); + client.close(); + } + + synchronized (mutex) { + mutex.set(mutex.get()-1); + mutex.notify(); + } + } + }.start(); + } + + synchronized (mutex) { + while (mutex.get() > 0) + try { + mutex.wait(); + } catch (InterruptedException e) { + assertFalse(e.getMessage(), true); + } + } + } + + @Test(timeout = 10000) + public void connectSendAndReceive() { + mutex = new AtomicLong(N_THREADS); + + for (int i = 0; i < N_THREADS; i++) { + new Thread() { + public void run() { + URI ws = null; + try { + ws = new URI("ws://localhost:9000/subscribe"); + } catch (URISyntaxException e) { + assertFalse(e.getMessage(), true); + } + MockSubscriptionHandler handler = new MockSubscriptionHandler(); + + SEPAWebsocketClient client = new SEPAWebsocketClient(ws, handler); + try { + client.connectBlocking(); + } catch (InterruptedException e) { + assertFalse(e.getMessage(), true); + } + if(client.isOpen()) { + Response ret = client.sendAndReceive("{\"subscribe\":{\"sparql\":\"select * where {?x ?y ?z}\",\"default-graph-uri\":\"http://sepatest\"}}", 2000); + + assertFalse(ret.toString(),ret.isError()); + + client.close(); + } + + synchronized (mutex) { + mutex.set(mutex.get()-1); + mutex.notify(); + } + } + }.start(); + } + + synchronized (mutex) { + while (mutex.get() > 0) + try { + mutex.wait(); + } catch (InterruptedException e) { + assertFalse(e.getMessage(), true); + } + } + } + +} diff --git a/client-api/src/test/resources/sepatest.jsap b/client-api/src/test/resources/sepatest.jsap index 72708ffa..92886be0 100644 --- a/client-api/src/test/resources/sepatest.jsap +++ b/client-api/src/test/resources/sepatest.jsap @@ -1,12 +1,12 @@ { "host": "localhost", "oauth": { - "enable" : true, + "enable" : false, "register": "https://localhost:8443/oauth/register", "tokenRequest": "https://localhost:8443/oauth/token" }, "sparql11protocol": { - "protocol": "https", + "protocol": "http", "port": 8000, "query": { "path": "/query", @@ -20,7 +20,7 @@ } }, "sparql11seprotocol": { - "protocol": "wss", + "protocol": "ws", "availableProtocols": { "ws": { "port": 9000, @@ -46,19 +46,31 @@ "DELETE_ALL" : { "sparql" : "delete where {?x ?y ?z}" }, - "U1": { + "VAIMEE": { "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" }, - "U2": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}" + "RANDOM": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + }, + "RANDOM1": { + "sparql": "delete {?s ?p ?o} insert {sepa:S1 sepa:P1 ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" } }, "queries": { - "Q1": { + "VAIMEE": { "sparql": "select * where {?x ?y \"ვაიმეე\"}" }, - "Q2": { + "ALL": { "sparql": "select * where {?x ?y ?z}" + }, + "RANDOM": { + "sparql": "select * where {sepa:S sepa:P ?random}" + }, + "RANDOM1": { + "sparql": "select * where {sepa:S1 sepa:P1 ?random}" + }, + "COUNT": { + "sparql": "select (COUNT(?x) AS ?n) where {?x ?y ?z}" } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java index 412a36ac..1aaa0359 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java @@ -1,79 +1,127 @@ package it.unibo.arces.wot.sepa.engine.bean; +import java.net.Inet4Address; +import java.net.UnknownHostException; import java.time.Duration; import java.util.Date; +import it.unibo.arces.wot.sepa.engine.core.EngineProperties; + public class EngineBeans { private static Date startDate = new Date(); - - private static String queryURL; - private static String updateURL; - - private static String secureQueryURL; - private static String secureUpdateURL; - private static String registrationURL; - private static String tokenRequestURL; - + + private static EngineProperties properties; + private static String version; - + public static void setVersion(String v) { version = v; } + public static String getVersion() { return version; } - - public static void setQueryURL(String s) { - queryURL = s; + + public static void setEngineProperties(EngineProperties prop) { + properties = prop; } - public static void setSecureQueryURL(String s) { - secureQueryURL = s; + + public static String getQueryPath() { + return properties.getQueryPath(); } - - public static String getQueryURL() { - return queryURL; + + public static String getUpdatePath() { + return properties.getUpdatePath(); } - - public static String getSecureQueryURL() { - return secureQueryURL; + + public static String getSubscribePath() { + return properties.getSubscribePath(); } - - public static void setUpdateURL(String s) { - updateURL = s; + + public static String getSecurePath() { + return properties.getSecurePath(); } - - public static void setSecureUpdateURL(String s) { - secureUpdateURL = s; + + public static String getRegisterPath() { + return properties.getRegisterPath(); } - - public static String getUpdateURL() { - return updateURL; + + public static String getTokenRequestPath() { + return properties.getTokenRequestPath(); } - - public static String getSecureUpdateURL() { - return secureUpdateURL; + + public static int getHttpPort() { + return properties.getHttpPort(); } - - public static void setRegistrationURL(String string) { - registrationURL = string; + + public static int getHttpsPort() { + return properties.getHttpsPort(); } - public static String getRegistrationURL() { - return registrationURL; + + public static int getWsPort() { + return properties.getWsPort(); } - - public static void setTokenRequestURL(String string) { - tokenRequestURL = string; + + public static int getWssPort() { + return properties.getWssPort(); } - public static String getTokenRequestURL() { - return tokenRequestURL; + + public static boolean getSecure() { + return properties.isSecure(); } - + public static String getUpTime() { return startDate.toString() + " " + Duration.between(startDate.toInstant(), new Date().toInstant()).toString(); } + public static void resetAll() { - ProcessorBeans.reset(); + QueryProcessorBeans.reset(); + UpdateProcessorBeans.reset(); SchedulerBeans.reset(); SubscribeProcessorBeans.reset(); } + + public static String getHost() { + try { + return Inet4Address.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + return "localhost"; + } + } + + public static String getQueryURL() { + String port = ""; + if (getHttpPort() != -1) port = ":"+getHttpPort(); + return "http://"+getHost()+port+getQueryPath(); + } + + public static String getUpdateURL() { + String port = ""; + if (getHttpPort() != -1) port = ":"+getHttpPort(); + return "http://"+getHost()+port+getUpdatePath(); + } + + public static String getSecureQueryURL() { + String port = ""; + if (getHttpsPort() != -1) port = ":"+getHttpsPort(); + return "https://"+getHost()+port+getSecurePath()+getQueryPath(); + } + + public static String getSecureUpdateURL() { + String port = ""; + if (getHttpsPort() != -1) port = ":"+getHttpsPort(); + return "https://"+getHost()+port+getSecurePath()+getUpdatePath(); + } + + public static String getRegistrationURL() { + String port = ""; + if (getHttpsPort() != -1) port = ":"+getHttpsPort(); + return "https://"+getHost()+port+getRegisterPath(); + } + + public static String getTokenRequestURL() { + String port = ""; + if (getHttpsPort() != -1) port = ":"+getHttpsPort(); + return "https://"+getHost()+port+getTokenRequestPath(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java index 1dd87b2d..afa806ed 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java @@ -7,8 +7,6 @@ import it.unibo.arces.wot.sepa.timing.Timings; public class HTTPHandlerBeans { - private long requests = 0; - private long timeoutRequests = 0; private long CORSFailedRequests = 0; private long parsingFailedRequests = 0; @@ -19,13 +17,11 @@ public class HTTPHandlerBeans { private float requestHandlingAverageTime = -1; private long requestHandlingMinTime = -1; private long requestHandlingMaxTime = -1; - private float handledRequests = 0; + private long handledRequests = 0; private HashMap timings = new HashMap(); public void reset() { - requests = 0; - timeoutRequests = 0; CORSFailedRequests = 0; parsingFailedRequests = 0; @@ -39,19 +35,19 @@ public void reset() { handledRequests = 0; } - public long start(HttpAsyncExchange handler) { - requests++; + public synchronized long start(HttpAsyncExchange handler) { long start = Timings.getTime(); timings.put(handler, start ); return start; } public synchronized long stop(HttpAsyncExchange handler) { - handledRequests++; + if (timings.get(handler) == null) return 0; requestHandlingTime = Timings.getTime() - timings.get(handler); + timings.remove(handler); if (requestHandlingMinTime == -1) @@ -71,27 +67,46 @@ else if (requestHandlingTime > requestHandlingMaxTime) / handledRequests; return requestHandlingTime; - } + private static long unitScale = 1000000; + + public static void scale_ms() { + unitScale = 1000000; + } + + public static void scale_us() { + unitScale = 1000; + } + + public static void scale_ns() { + unitScale = 1; + } + + public static String getUnitScale() { + if (unitScale == 1) return "ns"; + else if (unitScale == 1000) return "us"; + return "ms"; + } + public long getHandlingTime() { - return requestHandlingTime; + return requestHandlingTime/unitScale; } public long getHandlingMinTime() { - return requestHandlingMinTime; + return requestHandlingMinTime/unitScale; } public float getHandlingAvgTime() { - return requestHandlingAverageTime; + return requestHandlingAverageTime/unitScale; } public long getHandlingMaxTime_ms() { - return requestHandlingMaxTime; + return requestHandlingMaxTime/unitScale; } public long getRequests() { - return requests; + return handledRequests; } public long getErrors_Timeout() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java index 7c1683cb..f51d9da9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/ProcessorBeans.java @@ -4,36 +4,29 @@ public class ProcessorBeans { - private static int updateRequests = 0; - private static float queryMinTime = -1; - private static float queryAverageTime = -1; - private static float queryMaxTime = -1; - private static float queryTime = -1; - - private static int queryRequests = 0 ; - private static float updateMinTime = -1; - private static float updateAverageTime = -1; - private static float updateMaxTime = -1; - private static float updateTime = -1; - private static String host; private static int port; private static String queryPath; private static String updatePath; private static String updateMethod; private static String queryMethod; - private static int updateTimeout; - private static int queryTimeout; - public static void setEndpoint(SPARQL11Properties prop) { + private static int maxConcurrentRequests; + + public static void setEndpoint(SPARQL11Properties prop,int maxReq) { host = prop.getDefaultHost(); port = prop.getDefaultPort(); queryPath = prop.getDefaultQueryPath(); updatePath = prop.getUpdatePath(); updateMethod = prop.getUpdateMethod().name(); queryMethod = prop.getQueryMethod().name(); + + maxConcurrentRequests = maxReq; } + public static int getMaxConcurrentRequests() { + return maxConcurrentRequests; + } public static String getEndpointHost() { return host; } @@ -52,121 +45,4 @@ public static String getEndpointUpdateMethod() { public static String getEndpointQueryMethod() { return queryMethod; } - - public static void updateTimings(long start, long stop) { - updateTime = stop - start; - - updateRequests++; - - if (updateMinTime == -1) - updateMinTime = updateTime; - else if (updateTime < updateMinTime) - updateMinTime = updateTime; - - if (updateMaxTime == -1) - updateMaxTime = updateTime; - else if (updateTime > updateMaxTime) - updateMaxTime = updateTime; - - if (updateAverageTime == -1) - updateAverageTime = updateTime; - else - updateAverageTime = ((updateAverageTime * (updateRequests - 1)) + updateTime) / updateRequests; - } - - public static void queryTimings(long start, long stop) { - queryTime = stop - start; - - queryRequests++; - - if (queryMinTime == -1) - queryMinTime = queryTime; - else if (queryTime < queryMinTime) - queryMinTime = queryTime; - - if (queryMaxTime == -1) - queryMaxTime = queryTime; - else if (queryTime > queryMaxTime) - queryMaxTime = queryTime; - - if (queryAverageTime == -1) - queryAverageTime = queryTime; - else - queryAverageTime = ((queryAverageTime * (queryRequests - 1)) + queryTime) / queryRequests; - } - - public static void reset() { - updateRequests = 0; - queryRequests = 0 ; - - queryMinTime = -1; - queryAverageTime = -1; - queryMaxTime = -1; - queryTime = -1; - - updateMinTime = -1; - updateAverageTime = -1; - updateMaxTime = -1; - updateTime = -1; - } - - public static float getQueryTime_ms() { - return queryTime; - } - - public static float getUpdateTime_ms() { - return updateTime; - } - - public static long getProcessedRequests() { - return updateRequests+queryRequests; - } - - public static long getProcessedQueryRequests() { - return queryRequests; - } - - public static long getProcessedUpdateRequests() { - return updateRequests; - } - - public static float getTimings_QueryTime_Max_ms() { - return queryMaxTime; - } - - public static float getTimings_UpdateTime_Min_ms() { - return updateMinTime; - } - - public static float getTimings_UpdateTime_Average_ms() { - return updateAverageTime; - } - - public static float getTimings_UpdateTime_Max_ms() { - return updateMaxTime; - } - - public static float getTimings_QueryTime_Min_ms() { - return queryMinTime; - } - - public static float getTimings_QueryTime_Average_ms() { - return queryAverageTime; - } - - public static int getUpdateTimeout() { - return updateTimeout; - } - - public static int getQueryTimeout() { - return queryTimeout; - } - - public static void setUpdateTimeout(int t) { - updateTimeout = t; - } - - public static void setQueryTimeout(int t) { - queryTimeout = t; - } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/QueryProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/QueryProcessorBeans.java new file mode 100644 index 00000000..5a68480e --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/QueryProcessorBeans.java @@ -0,0 +1,90 @@ +package it.unibo.arces.wot.sepa.engine.bean; + +public class QueryProcessorBeans { + + private static int requests = 0; + private static float min = -1; + private static float average = -1; + private static float max = -1; + private static float current = -1; + + private static int timeout; + + private static long unitScale = 1000000; + + public static void scale_ms() { + unitScale = 1000000; + } + + public static void scale_us() { + unitScale = 1000; + } + + public static void scale_ns() { + unitScale = 1; + } + + public static String getUnitScale() { + if (unitScale == 1) return "ns"; + else if (unitScale == 1000) return "us"; + return "ms"; + } + + public synchronized static void timings(long start, long stop) { + current = stop-start; + + requests++; + + if (min == -1) + min = current; + else if (current < min) + min = current; + + if (max == -1) + max = current; + else if (current > max) + max = current; + + if (average == -1) + average = current; + else + average = ((average * (requests - 1)) + current) / requests; + } + + public static void reset() { + requests = 0; + + min = -1; + average = -1; + max = -1; + current = -1; + } + + public static float getCurrent() { + return current/unitScale; + } + + public static float getMax() { + return max/unitScale; + } + + public static float getMin() { + return min/unitScale; + } + + public static float getAverage() { + return average/unitScale; + } + + public static void setTimeout(int t) { + timeout = t; + } + + public static int getTimeout() { + return timeout; + } + + public static long getRequests() { + return requests; + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java index 8923594c..da2930d5 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java @@ -1,7 +1,5 @@ package it.unibo.arces.wot.sepa.engine.bean; -import java.time.Instant; - public class SubscribeProcessorBeans { private static long requests = 0; @@ -13,15 +11,32 @@ public class SubscribeProcessorBeans { private static long activeSPUs = 0; private static long maxActiveSPUs = 0; - private static int keepalive = 5000; - private static long subscribeRequests; - private static long unsubscribeRequests; private static int SPUProcessingTimeout; - public static long getRequests() { + private static long unitScale = 1000000; + + public static void scale_ms() { + unitScale = 1000000; + } + + public static void scale_us() { + unitScale = 1000; + } + + public static void scale_ns() { + unitScale = 1; + } + + public static String getUnitScale() { + if (unitScale == 1) return "ns"; + else if (unitScale == 1000) return "us"; + return "ms"; + } + + public static long getUpdateRequests() { return requests; } @@ -33,10 +48,6 @@ public static long getSPUs_max() { return maxActiveSPUs; } - public static float getSPUs_time() { - return time; - } - public static void setActiveSPUs(long n) { activeSPUs = n; if (activeSPUs > maxActiveSPUs) maxActiveSPUs = activeSPUs; @@ -50,9 +61,9 @@ public static void unsubscribeRequest() { unsubscribeRequests++; } - public static void timings(Instant start, Instant stop) { + public synchronized static void timings(long start, long stop) { requests++; - time = stop.toEpochMilli() - start.toEpochMilli(); + time = stop - start; if (minTime == -1) minTime = time; @@ -81,24 +92,20 @@ public static void reset() { unsubscribeRequests = 0; } - public static void setKeepalive(int keepAlivePeriod) { - keepalive = keepAlivePeriod; + public static float getSPUs_time() { + return time/unitScale; } - public static int getKeepalive(){ - return keepalive; - } - public static float getSPUs_time_min() { - return minTime; + return minTime/unitScale; } public static float getSPUs_time_max() { - return maxTime; + return maxTime/unitScale; } public static float getSPUs_time_averaae() { - return averageTime; + return averageTime/unitScale; } public static long getSubscribeRequests() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/UpdateProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/UpdateProcessorBeans.java new file mode 100644 index 00000000..df8b5d61 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/UpdateProcessorBeans.java @@ -0,0 +1,102 @@ +package it.unibo.arces.wot.sepa.engine.bean; + +public class UpdateProcessorBeans { + + private static long requests = 0; + private static float min = -1; + private static float average = -1; + private static float max = -1; + private static float current = -1; + + private static long timeout; + + private static long unitScale = 1000000; + + private static boolean reliable = true; + + public static void scale_ms() { + unitScale = 1000000; + } + + public static void scale_us() { + unitScale = 1000; + } + + public static void scale_ns() { + unitScale = 1; + } + + public static String getUnitScale() { + if (unitScale == 1) + return "ns"; + else if (unitScale == 1000) + return "us"; + return "ms"; + } + + public synchronized static void timings(long start, long stop) { + current = stop - start; + + requests++; + + if (min == -1) + min = current; + else if (current < min) + min = current; + + if (max == -1) + max = current; + else if (current > max) + max = current; + + if (average == -1) + average = current; + else + average = ((average * (requests - 1)) + current) / requests; + } + + public static void reset() { + requests = 0; + + min = -1; + average = -1; + max = -1; + current = -1; + } + + public static float getCurrent() { + return current/unitScale; + } + + public static float getMax() { + return max/unitScale; + } + + public static float getMin() { + return min/unitScale; + } + + public static float getAverage() { + return average/unitScale; + } + + public static void setTimeout(long t) { + timeout = t; + } + + public static long getTimeout() { + return timeout; + } + + public static long getRequests() { + return requests; + } + + public static void setReilable(boolean updateReliable) { + reliable = updateReliable; + } + + public static boolean getReilable() { + return reliable; + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java index e0fe8c78..f324c70e 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.bean; -import java.time.Instant; +import it.unibo.arces.wot.sepa.timing.Timings; public class WebsocketBeans { private long messages = 0; @@ -21,10 +21,10 @@ public class WebsocketBeans { private long handledunsubscribes = 0; - public long unsubscribeTimings(Instant start) { + public long unsubscribeTimings(long start) { handledunsubscribes++; - unsubscribeHandlingTime = Instant.now().toEpochMilli() - start.toEpochMilli(); + unsubscribeHandlingTime = Timings.getTime() - start; if (unsubscribeHandlingMinTime == -1) unsubscribeHandlingMinTime = unsubscribeHandlingTime; @@ -45,10 +45,10 @@ else if (unsubscribeHandlingTime > unsubscribeHandlingMaxTime) return unsubscribeHandlingTime; } - public long subscribeTimings(Instant start) { + public long subscribeTimings(long start) { handledSubscribes++; - subscribeHandlingTime = Instant.now().toEpochMilli() - start.toEpochMilli(); + subscribeHandlingTime = Timings.getTime() - start; if (subscribeHandlingMinTime == -1) subscribeHandlingMinTime = subscribeHandlingTime; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 705762f1..98b31206 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -28,6 +28,8 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.PatternSyntaxException; import org.apache.logging.log4j.LogManager; @@ -40,12 +42,10 @@ import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; import it.unibo.arces.wot.sepa.engine.bean.EngineBeans; -import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; -import it.unibo.arces.wot.sepa.engine.processing.Processor; - +import it.unibo.arces.wot.sepa.engine.processing.ProcessorThread; import it.unibo.arces.wot.sepa.engine.protocol.websocket.WebsocketServer; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpGate; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpsGate; @@ -55,7 +55,7 @@ import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; /** - * This class represents the SPARQL Subscription Broker (Core) of the Semantic + * This class represents the SPARQL Subscription Broker (Core) of the SPARQL * Event Processing Architecture (SEPA) * * @author Luca Roffia (luca.roffia@unibo.it) @@ -70,11 +70,14 @@ public class Engine implements EngineMBean { // Scheduler request queue private final SchedulerRequestResponseQueue schedulerQueue = new SchedulerRequestResponseQueue(); + // Broken spuids queue + private final BlockingQueue killSpuids = new LinkedBlockingQueue(); + // Primitives scheduler/dispatcher private Scheduler scheduler = null; // Primitives scheduler/dispatcher - private Processor processor = null; + private ProcessorThread processor = null; // SPARQL 1.1 Protocol handler private HttpGate httpGate = null; @@ -85,7 +88,7 @@ public class Engine implements EngineMBean { private HttpsGate httpsGate = null; private int wsShutdownTimeout = 5000; - // Outh 2.0 Authorization Server + // Oauth 2.0 Authorization Server private AuthorizationManager oauth; // Dependability manager @@ -97,6 +100,8 @@ public class Engine implements EngineMBean { private String jwtAlias = "sepakey"; private String jwtPassword = "sepa2017"; private String serverCertificate = "sepacert"; + + // Properties files private String engineJpar = "engine.jpar"; private String endpointJpar = "endpoint.jpar"; @@ -215,18 +220,14 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException // Initialize SPARQL 1.1 SE processing service properties try { properties = new EngineProperties(engineJpar); - } catch (SEPAPropertiesException e) { - // System.err.println("Failed to load engine.jpar: "+e.getMessage()); - // properties = null; - } + } catch (SEPAPropertiesException e) {} + EngineBeans.setEngineProperties(properties); + SPARQL11Properties endpointProperties = null; try { endpointProperties = new SPARQL11Properties(endpointJpar ); - } catch (SEPAPropertiesException e2) { - // System.err.println("Failed to load endpoint.jpar: "+e2.getMessage()); - // endpointProperties = null; - } + } catch (SEPAPropertiesException e2) {} // OAUTH 2.0 Authorization Manager if (properties.isSecure()) { @@ -244,11 +245,11 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException scheduler.start(); // Dependability manager - dependabilityMng = new DependabilityManager(scheduler); + dependabilityMng = new DependabilityManager(killSpuids); // SEPA Processor try { - processor = new Processor(endpointProperties, properties, schedulerQueue); + processor = new ProcessorThread(endpointProperties, properties, schedulerQueue,killSpuids); } catch (SEPAProtocolException e1) { System.err.println(e1.getMessage()); System.exit(1); @@ -398,72 +399,67 @@ public String getUpTime() { } @Override - public String getURL_Query() { - return EngineBeans.getQueryURL(); + public void resetAll() { + EngineBeans.resetAll(); } @Override - public String getURL_Update() { - return EngineBeans.getUpdateURL(); + public String getVersion() { + return EngineBeans.getVersion(); } - + @Override - public String getURL_SecureQuery() { - return EngineBeans.getSecureQueryURL(); + public String getQueryPath() { + return EngineBeans.getQueryPath(); } - + @Override - public String getURL_SecureUpdate() { - return EngineBeans.getSecureUpdateURL(); + public String getUpdatePath() { + return EngineBeans.getUpdatePath(); } - + @Override - public String getURL_Registration() { - return EngineBeans.getRegistrationURL(); + public String getSubscribePath() { + return EngineBeans.getSubscribePath(); } @Override - public String getURL_TokenRequest() { - return EngineBeans.getTokenRequestURL(); + public String getSecurePath() { + return EngineBeans.getSecurePath(); } - + @Override - public void resetAll() { - EngineBeans.resetAll(); - } - - @Override - public String getEndpoint_Host() { - return ProcessorBeans.getEndpointHost(); + public String getRegisterPath() { + return EngineBeans.getRegisterPath(); } - + @Override - public String getEndpoint_Port() { - return String.format("%d", ProcessorBeans.getEndpointPort()); + public String getTokenRequestPath() { + return EngineBeans.getTokenRequestPath(); } - + @Override - public String getEndpoint_QueryPath() { - return ProcessorBeans.getEndpointQueryPath(); + public int getHttpPort() { + return EngineBeans.getHttpPort(); } - + @Override - public String getEndpoint_UpdatePath() { - return ProcessorBeans.getEndpointUpdatePath(); + public int getHttpsPort() { + return EngineBeans.getHttpsPort(); } - + @Override - public String getEndpoint_UpdateMethod() { - return ProcessorBeans.getEndpointUpdateMethod(); + public int getWsPort() { + return EngineBeans.getWsPort(); } - + @Override - public String getEndpoint_QueryMethod() { - return ProcessorBeans.getEndpointQueryMethod(); + public int getWssPort() { + return EngineBeans.getWssPort(); } - + @Override - public String getVersion() { - return EngineBeans.getVersion(); + public boolean getSecure() { + return EngineBeans.getSecure(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineMBean.java index b8a83e29..5750c113 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineMBean.java @@ -22,26 +22,27 @@ public interface EngineMBean { public String getUpTime(); - public String getURL_Query(); - public String getURL_Update(); + public String getQueryPath(); - public String getURL_SecureQuery(); - public String getURL_SecureUpdate(); + public String getUpdatePath(); - public String getURL_Registration(); - public String getURL_TokenRequest(); - - public String getEndpoint_Host(); - - public String getEndpoint_Port(); - - public String getEndpoint_QueryPath(); + public String getSubscribePath(); - public String getEndpoint_UpdatePath(); - - public String getEndpoint_UpdateMethod(); - - public String getEndpoint_QueryMethod(); + public String getSecurePath(); + + public String getRegisterPath(); + + public String getTokenRequestPath(); + + public int getHttpPort(); + + public int getHttpsPort(); + + public int getWsPort(); + + public int getWssPort(); + + public boolean getSecure(); public void resetAll(); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java index 7abc6d60..f8c5c7ec 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java @@ -17,7 +17,6 @@ */ package it.unibo.arces.wot.sepa.engine.core; -//import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -34,13 +33,40 @@ import org.apache.logging.log4j.LogManager; /** - * { "parameters": { "scheduler": { "queueSize": 100 }, "processor": { - * "updateTimeout": 5000, "queryTimeout": 5000, "maxConcurrentRequests": 5 }, - * "spu": { "timeout": 2000 }, "gates": { "secure": false, "ports": { "http": - * 8000, "ws": 9000, "https": 8443, "wss": 9443 }, "paths": { "update": - * "/update", "query": "/query", "subscribe": "/subscribe", "register": - * "/oauth/register", "tokenRequest": "/oauth/token", "securePath": "/secure" } - * } } } + *
    +{
    +	"parameters": {
    +		"scheduler": {
    +			"queueSize": 100
    +		},
    +		"processor": {
    +			"updateTimeout": 60000,
    +			"queryTimeout": 60000,
    +			"maxConcurrentRequests": 5
    +		},
    +		"spu": {
    +			"timeout": 5000
    +		},
    +		"gates": {
    +			"secure": false,
    +			"paths": {
    +				"securePath": "/secure",
    +				"update": "/update",
    +				"query": "/query",
    +				"subscribe": "/subscribe",
    +				"register": "/oauth/register",
    +				"tokenRequest": "/oauth/token"
    +			},
    +			"ports": {
    +				"http": 8000,
    +				"https": 8443,
    +				"ws": 9000,
    +				"wss": 9443
    +			}
    +		}
    +	}
    +}
    + * 
    */ public class EngineProperties { private static final Logger logger = LogManager.getLogger(); @@ -306,4 +332,12 @@ public int getSPUProcessingTimeout() { } } + public boolean isUpdateReliable() { + try { + return properties.get("processor").getAsJsonObject().get("reliableUpdate").getAsBoolean(); + } catch (Exception e) { + return true; + } + } + } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java index 151eb465..2ab8fbee 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java @@ -2,34 +2,49 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.concurrent.BlockingQueue; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.java_websocket.WebSocket; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; - public class DependabilityManager { - private static final Logger logger = LogManager.getLogger("DependabilityManager"); + private static final Logger logger = LogManager.getLogger(); // Active sockets private HashMap> subscriptions = new HashMap>(); - // To send unsubscribe requests for broken sockets - private Scheduler scheduler; + // Broken sockets + private ArrayList brokenSockets = new ArrayList(); - public DependabilityManager(Scheduler scheduler) { - this.scheduler = scheduler; + private final BlockingQueue killSpuids; + + public DependabilityManager(BlockingQueue spuids) { + this.killSpuids = spuids; } public void onSubscribe(WebSocket conn, String spuid) { + logger.debug("@onSubscribe: "+conn+" SPUID: "+spuid); + + if (brokenSockets.contains(conn)) { + logger.debug("Socket has been closed: "+conn+" SPUID: "+spuid); + logger.debug("Request to kill SPU: "+spuid); + try { + killSpuids.put(spuid); + } catch (InterruptedException e) { + logger.warn(e.getMessage()); + } + return; + } + if (!subscriptions.containsKey(conn)) subscriptions.put(conn, new ArrayList()); subscriptions.get(conn).add(spuid); } public void onUnsubscribe(WebSocket conn, String spuid) { + logger.debug("@onUnsubscribe: "+conn+" SPUID: "+spuid); + if (subscriptions.containsKey(conn)) { subscriptions.get(conn).remove(spuid); if (subscriptions.get(conn).isEmpty()) @@ -38,25 +53,26 @@ public void onUnsubscribe(WebSocket conn, String spuid) { } public void onBrokenSocket(WebSocket conn) { - if (!subscriptions.containsKey(conn)) return; + logger.debug("@onBrokenSocket: "+conn); + + if (!subscriptions.containsKey(conn)) { + brokenSockets.add(conn); + return; + } - logger.info(String.format("Broken socket with %d active subscriptions", subscriptions.get(conn).size())); + logger.debug(String.format("Broken socket with %d active subscriptions", subscriptions.get(conn).size())); - // Kill all the SPUs + // Kill all SPUs for (String spuid : subscriptions.get(conn)) { - scheduler.schedule(new UnsubscribeRequest(spuid), null); + logger.debug("Request to kill SPU: "+spuid); + try { + killSpuids.put(spuid); + } catch (InterruptedException e) { + logger.warn(e.getMessage()); + } } // Remove subscriptions subscriptions.remove(conn); } - - /* - * @Override public void sendResponse(Response response) throws IOException { if - * (response.isUnsubscribeResponse()) { UnsubscribeResponse ret = - * (UnsubscribeResponse) response; String spuid = ret.getSpuid(); - * synchronized(unsubscribeList) { unsubscribeList.remove(spuid); - * logger.info(String.format("Pending unsubscribe requests: %d", - * unsubscribeList.size())); } } } - */ } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java deleted file mode 100644 index 28311971..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java +++ /dev/null @@ -1,26 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -public interface ProcessorMBean { - - public void reset(); - - public long getProcessedRequests(); - public long getProcessedQueryRequests(); - public long getProcessedUpdateRequests(); - - public float getTimings_UpdateTime_ms(); - public float getTimings_UpdateTime_Min_ms(); - public float getTimings_UpdateTime_Average_ms(); - public float getTimings_UpdateTime_Max_ms(); - - public float getTimings_QueryTime_ms(); - public float getTimings_QueryTime_Min_ms(); - public float getTimings_QueryTime_Average_ms(); - public float getTimings_QueryTime_Max_ms(); - - public int getUpdateTimeout(); - public int getQueryTimeout(); - - public void setUpdateTimeout(int t); - public void setQueryTimeout(int t); -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java similarity index 56% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java index ebf6d852..ae6310da 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java @@ -18,7 +18,7 @@ package it.unibo.arces.wot.sepa.engine.processing; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.Semaphore; import org.apache.logging.log4j.Logger; @@ -33,7 +33,9 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.QueryProcessorBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; +import it.unibo.arces.wot.sepa.engine.bean.UpdateProcessorBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.EventHandler; import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; @@ -41,78 +43,60 @@ import org.apache.logging.log4j.LogManager; -public class Processor extends Thread implements ProcessorMBean { +public class ProcessorThread extends Thread implements ProcessorThreadMBean { private final Logger logger = LogManager.getLogger(); // Processors - private final UpdateProcessor updateProcessor; + private final UpdateProcessingThread updateProcessor; private final QueryProcessor queryProcessor; private final SubscribeProcessor subscribeProcessor; + + // Broken SPU killer + private final SpuKillerThread spuKiller; // Scheduler queue - private SchedulerRequestResponseQueue queue; + private SchedulerRequestResponseQueue schedulerQueue; // Concurrent endpoint limit private Semaphore endpointSemaphore = null; - // Update queue - private LinkedBlockingQueue updateQueue = new LinkedBlockingQueue(); - - public Processor(SPARQL11Properties endpointProperties, EngineProperties properties, - SchedulerRequestResponseQueue queue) throws IllegalArgumentException, SEPAProtocolException { + public ProcessorThread(SPARQL11Properties endpointProperties, EngineProperties properties, + SchedulerRequestResponseQueue queue,BlockingQueue killSpuids) throws IllegalArgumentException, SEPAProtocolException { if (queue == null) { logger.error("Queue is null"); throw new IllegalArgumentException("Queue is null"); } - this.queue = queue; + this.schedulerQueue = queue; + // TODO: extending at run-time the semaphore max // Number of maximum concurrent requests (supported by the endpoint) int max = properties.getMaxConcurrentRequests(); if (max > 0) endpointSemaphore = new Semaphore(max, true); - // Update processor - updateProcessor = new UpdateProcessor(endpointProperties, endpointSemaphore); - // Query processor queryProcessor = new QueryProcessor(endpointProperties, endpointSemaphore); // SPU manager subscribeProcessor = new SubscribeProcessor(endpointProperties, properties, endpointSemaphore); + // Update processor + if (properties.isUpdateReliable()) + updateProcessor = new UpdateProcessingThread(new UpdateProcessor(endpointProperties, endpointSemaphore), + subscribeProcessor, queue); + else + updateProcessor = new UpdateProcessingThread(new UpdateProcessor(endpointProperties, endpointSemaphore), + subscribeProcessor, null); + + // SPU killer + spuKiller = new SpuKillerThread(killSpuids,subscribeProcessor); + // JMX SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); - ProcessorBeans.setEndpoint(endpointProperties); - ProcessorBeans.setQueryTimeout(properties.getQueryTimeout()); - ProcessorBeans.setUpdateTimeout(properties.getUpdateTimeout()); - - // Main thread for FIFO scheduling of updates - Thread updateScheduler = new Thread() { - public void run() { - while(true) { - UpdateRequest request; - try { - request = updateQueue.take(); - } catch (InterruptedException e) { - return; - } - - // Process update request - request.setTimeout(ProcessorBeans.getUpdateTimeout()); - Response ret = updateProcessor.process(request); - - // Notify update result - queue.addResponse(ret); - - // Subscription processing - if (ret.isUpdateResponse()) { - subscribeProcessor.process((UpdateResponse) ret); - } - } - } - }; - updateScheduler.setName("SEPA-Update-Scheduler"); - updateScheduler.start(); + ProcessorBeans.setEndpoint(endpointProperties, max); + QueryProcessorBeans.setTimeout(properties.getQueryTimeout()); + UpdateProcessorBeans.setTimeout(properties.getUpdateTimeout()); + UpdateProcessorBeans.setReilable(properties.isUpdateReliable()); } @Override @@ -121,29 +105,35 @@ public void run() { // WAIT NEW REQUEST ScheduledRequest scheduledRequest; try { - scheduledRequest = queue.waitRequest(); + scheduledRequest = schedulerQueue.waitRequest(); } catch (InterruptedException e1) { return; } - Request request = scheduledRequest.getRequest(); if (request.isUpdateRequest()) { logger.debug("Update request #" + request.getToken()); logger.trace(request); - // Put the request into the FIFO queue - updateQueue.offer((UpdateRequest) request); + // Update response QoS + if (UpdateProcessorBeans.getReilable()) { + updateProcessor.setSchedulerQueue(schedulerQueue); + } + else { + updateProcessor.setSchedulerQueue(null); + schedulerQueue.addResponse(new UpdateResponse(request.getToken(),"{\"Request scheduled for processing\"}")); + } + + // Add a new UpdateRequest to be processed + updateProcessor.process((UpdateRequest) request); } else if (request.isQueryRequest()) { logger.debug("Query request #" + request.getToken()); logger.trace(request); - request.setTimeout(ProcessorBeans.getQueryTimeout()); - Thread queryProcessing = new Thread() { public void run() { Response ret = queryProcessor.process((QueryRequest) request); - queue.addResponse(ret); + schedulerQueue.addResponse(ret); } }; queryProcessing.setName("SEPA-Query-Processing-Thread-" + request.getToken()); @@ -155,14 +145,14 @@ public void run() { Response ret = subscribeProcessor.subscribe((SubscribeRequest) request, (EventHandler) scheduledRequest.getHandler()); - queue.addResponse(ret); + schedulerQueue.addResponse(ret); } else if (request.isUnsubscribeRequest()) { logger.info("Unsubscribe request #" + request.getToken()); logger.debug(request); Response ret = subscribeProcessor.unsubscribe((UnsubscribeRequest) request); - queue.addResponse(ret); + schedulerQueue.addResponse(ret); } } } @@ -171,91 +161,54 @@ public void run() { public synchronized void start() { super.start(); subscribeProcessor.start(); + updateProcessor.start(); + spuKiller.start(); } @Override public void interrupt() { super.interrupt(); subscribeProcessor.stop(); + + updateProcessor.finish(); + updateProcessor.interrupt(); + + spuKiller.finish(); + spuKiller.interrupt(); } @Override - public void reset() { - ProcessorBeans.reset(); - } - - @Override - public float getTimings_UpdateTime_ms() { - return ProcessorBeans.getUpdateTime_ms(); - } - - @Override - public float getTimings_QueryTime_ms() { - return ProcessorBeans.getQueryTime_ms(); - } - - @Override - public long getProcessedRequests() { - return ProcessorBeans.getProcessedRequests(); - } - - @Override - public long getProcessedQueryRequests() { - return ProcessorBeans.getProcessedQueryRequests(); - } - - @Override - public long getProcessedUpdateRequests() { - return ProcessorBeans.getProcessedUpdateRequests(); - } - - @Override - public float getTimings_UpdateTime_Min_ms() { - return ProcessorBeans.getTimings_UpdateTime_Min_ms(); - } - - @Override - public float getTimings_UpdateTime_Average_ms() { - return ProcessorBeans.getTimings_UpdateTime_Average_ms(); - } - - @Override - public float getTimings_UpdateTime_Max_ms() { - return ProcessorBeans.getTimings_UpdateTime_Max_ms(); - } - - @Override - public float getTimings_QueryTime_Min_ms() { - return ProcessorBeans.getTimings_QueryTime_Min_ms(); + public String getEndpointHost() { + return ProcessorBeans.getEndpointHost(); } @Override - public float getTimings_QueryTime_Average_ms() { - return ProcessorBeans.getTimings_QueryTime_Average_ms(); + public int getEndpointPort() { + return ProcessorBeans.getEndpointPort(); } @Override - public float getTimings_QueryTime_Max_ms() { - return ProcessorBeans.getTimings_QueryTime_Max_ms(); + public String getEndpointQueryPath() { + return ProcessorBeans.getEndpointQueryPath(); } @Override - public int getUpdateTimeout() { - return ProcessorBeans.getUpdateTimeout(); + public String getEndpointUpdatePath() { + return ProcessorBeans.getEndpointUpdatePath(); } @Override - public int getQueryTimeout() { - return ProcessorBeans.getQueryTimeout(); + public String getEndpointUpdateMethod() { + return ProcessorBeans.getEndpointUpdateMethod(); } @Override - public void setUpdateTimeout(int t) { - ProcessorBeans.setUpdateTimeout(t); + public String getEndpointQueryMethod() { + return ProcessorBeans.getEndpointQueryMethod(); } @Override - public void setQueryTimeout(int t) { - ProcessorBeans.setQueryTimeout(t); + public int getMaxConcurrentRequests() { + return ProcessorBeans.getMaxConcurrentRequests(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java new file mode 100644 index 00000000..2dbe18d1 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java @@ -0,0 +1,11 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +public interface ProcessorThreadMBean { + public String getEndpointHost(); + public int getEndpointPort(); + public String getEndpointQueryPath(); + public String getEndpointUpdatePath(); + public String getEndpointUpdateMethod(); + public String getEndpointQueryMethod(); + public int getMaxConcurrentRequests(); +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index 6460d60b..036a07ac 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -32,10 +32,11 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; -import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.QueryProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.timing.Timings; -public class QueryProcessor { +public class QueryProcessor implements QueryProcessorMBean { private static final Logger logger = LogManager.getLogger(); private SPARQL11Protocol endpoint; @@ -46,6 +47,8 @@ public QueryProcessor(SPARQL11Properties properties, Semaphore endpointSemaphore this.endpoint = new SPARQL11Protocol(); this.endpointSemaphore = endpointSemaphore; this.properties = properties; + + SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); } public synchronized Response process(QueryRequest req) { @@ -73,7 +76,7 @@ public synchronized Response process(QueryRequest req) { QueryRequest request; request = new QueryRequest(req.getToken(), properties.getQueryMethod(), properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), properties.getDefaultQueryPath(), - req.getSPARQL(), req.getTimeout(), req.getDefaultGraphUri(), req.getNamedGraphUri(), + req.getSPARQL(), QueryProcessorBeans.getTimeout(), req.getDefaultGraphUri(), req.getNamedGraphUri(), authorizationHeader); ret = endpoint.query(request); @@ -82,10 +85,72 @@ public synchronized Response process(QueryRequest req) { endpointSemaphore.release(); long stop = Timings.getTime(); + logger.trace("Response: " + ret.toString()); Timings.log("QUERY_PROCESSING_TIME", start, stop); - ProcessorBeans.queryTimings(start, stop); + QueryProcessorBeans.timings(start, stop); return ret; } + + @Override + public void reset() { + QueryProcessorBeans.reset(); + } + + @Override + public long getRequests() { + return QueryProcessorBeans.getRequests(); + } + + @Override + public float getTimingsCurrent() { + return QueryProcessorBeans.getCurrent(); + } + + @Override + public float getTimingsMin() { + return QueryProcessorBeans.getMin(); + } + + @Override + public float getTimingsAverage() { + return QueryProcessorBeans.getAverage(); + } + + @Override + public float getTimingsMax() { + return QueryProcessorBeans.getMax(); + } + + @Override + public int getTimeout() { + return QueryProcessorBeans.getTimeout(); + } + + @Override + public void setTimeout(int t) { + QueryProcessorBeans.setTimeout(t); + } + + @Override + public void scale_ms() { + QueryProcessorBeans.scale_ms(); + + } + + @Override + public void scale_us() { + QueryProcessorBeans.scale_us(); + } + + @Override + public void scale_ns() { + QueryProcessorBeans.scale_ns(); + } + + @Override + public String getUnitScale() { + return QueryProcessorBeans.getUnitScale(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessorMBean.java new file mode 100644 index 00000000..5973e3b0 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessorMBean.java @@ -0,0 +1,21 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +public interface QueryProcessorMBean { + + public void reset(); + + public long getRequests(); + + public float getTimingsCurrent(); + public float getTimingsMin(); + public float getTimingsAverage(); + public float getTimingsMax(); + + public int getTimeout(); + public void setTimeout(int t); + + public void scale_ms(); + public void scale_us(); + public void scale_ns(); + public String getUnitScale(); +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java new file mode 100644 index 00000000..47b953a0 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java @@ -0,0 +1,35 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; + +public class SpuKillerThread extends Thread { + private final AtomicBoolean end = new AtomicBoolean(false); + + private BlockingQueue queue; + private SubscribeProcessor processor; + + public SpuKillerThread(BlockingQueue killSpuids,SubscribeProcessor processor) { + this.queue = killSpuids; + this.processor = processor; + } + + public void run() { + while (!end.get()) { + String spuid; + try { + spuid = queue.take(); + } catch (InterruptedException e) { + return; + } + + processor.unsubscribe(new UnsubscribeRequest(spuid)); + } + } + + public void finish(){ + end.set(true); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java index c7f199d4..cb12aae0 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java @@ -19,7 +19,6 @@ package it.unibo.arces.wot.sepa.engine.processing; import java.io.IOException; -import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.Semaphore; @@ -47,6 +46,7 @@ import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUNaive; +import it.unibo.arces.wot.sepa.timing.Timings; class SubscribeProcessor implements SubscribeProcessorMBean, EventHandler { private final Logger logger = LogManager.getLogger(); @@ -56,17 +56,12 @@ class SubscribeProcessor implements SubscribeProcessorMBean, EventHandler { private SPUManager spuManager = new SPUManager(); - // REAL spuid => list of FAKE spuids - private HashMap> spuidPoll = new HashMap>(); + // Maps + private HashMap> activeSpus = new HashMap>(); + private HashMap spuids = new HashMap(); - // FAKE spuid ==> handler private HashMap handlers = new HashMap(); - - // Handler ==> sequence number - private HashMap sequenceNumbers = new HashMap(); - - // FAKE spuid ==> REAL spuid - private HashMap fakeMap = new HashMap(); + private HashMap sequenceNumbers = new HashMap(); public SubscribeProcessor(SPARQL11Properties endpointProperties, EngineProperties engineProperties, Semaphore endpointSemaphore) { @@ -92,18 +87,48 @@ public void sendResponse(Response response) throws IOException { @Override public void notifyEvent(Notification notify) throws IOException { - for (String spuid : spuidPoll.get(notify.getSpuid())) { - EventHandler handler = handlers.get(spuid); - - handler.notifyEvent(new Notification(spuid, notify.getARBindingsResults(), sequenceNumbers.get(handler))); - - sequenceNumbers.put(handler, sequenceNumbers.get(handler) + 1); + synchronized (handlers) { + if (!activeSpus.containsKey(notify.getSpuid())) + return; + + // Notify all subscribed clients + ArrayList toBeRemoved = new ArrayList(); + for (String spuid : activeSpus.get(notify.getSpuid())) { + EventHandler handler = handlers.get(spuid); + + if (handler != null) { + logger.debug("Notify: " + spuid); + handler.notifyEvent( + new Notification(spuid, notify.getARBindingsResults(), sequenceNumbers.get(spuid))); + sequenceNumbers.put(spuid, sequenceNumbers.get(spuid) + 1); + } else { + logger.debug("Unregister SPU handler: " + spuid); + + spuids.remove(spuid); + sequenceNumbers.remove(spuid); + handlers.remove(spuid); + + toBeRemoved.add(spuid); + } + } + + // Remove SPUID + for (String spuid : toBeRemoved) { + activeSpus.get(notify.getSpuid()).remove(spuid); + logger.debug(notify.getSpuid() + " number of clients: " + activeSpus.get(notify.getSpuid()).size()); + if (activeSpus.get(notify.getSpuid()).isEmpty()) { + activeSpus.remove(notify.getSpuid()); + + // Deactivate SPU + spuManager.deactivate(notify.getSpuid()); + } + } } } - public void process(UpdateResponse update) { + public synchronized void process(UpdateResponse update) { logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); - Instant start = Instant.now(); + long start = Timings.getTime(); // Start subscription processing spuManager.startProcessing(update); @@ -111,23 +136,25 @@ public void process(UpdateResponse update) { // Wait all SPUs completing processing (or timeout) spuManager.waitEndOfProcessing(); - Instant stop = Instant.now(); + long stop = Timings.getTime(); SubscribeProcessorBeans.timings(start, stop); logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); } - - public Response subscribe(SubscribeRequest req, EventHandler handler) { + + public synchronized Response subscribe(SubscribeRequest req, EventHandler handler) { logger.trace(req.toString()); SubscribeProcessorBeans.subscribeRequest(); // Is SPU already available or do we need to create a new one? ISPU spu = spuManager.getSPU(req); - if (spu == null) spu = createSPU(req); - if (spu == null) return new ErrorResponse(req.getToken(), 500, "SPU creation failed: " + req.toString()); - + if (spu == null) + spu = createSPU(req); + if (spu == null) + return new ErrorResponse(req.getToken(), 500, "Failed to create SPU " + req.toString()); + // Generate a fake SPU id String spuid = spuManager.generateSpuid(); @@ -136,33 +163,26 @@ public Response subscribe(SubscribeRequest req, EventHandler handler) { return new SubscribeResponse(req.getToken(), spuid, spu.getLastBindings()); } - - public Response unsubscribe(UnsubscribeRequest req) { + + public synchronized Response unsubscribe(UnsubscribeRequest req) { logger.trace(req); SubscribeProcessorBeans.unsubscribeRequest(); String spuid = req.getSubscribeUUID(); - String masterSpuid = fakeMap.get(spuid); + String masterSpuid = spuids.get(spuid); logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); - - if (masterSpuid == null) return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + spuid); - if (!spuManager.isValidSpuId(masterSpuid)) return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + masterSpuid); + + if (masterSpuid == null) + return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + spuid); // Unregister handler unregisterHandler(masterSpuid, spuid); - if (spuidPoll.get(masterSpuid).isEmpty()) { - spuidPoll.remove(masterSpuid); - - // Deactivate SPU - spuManager.deactivate(masterSpuid); - } - return new UnsubscribeResponse(req.getToken(), spuid); } - + // TODO: choose different kinds of SPU based on subscribe request private ISPU createSPU(SubscribeRequest req) { ISPU spu; @@ -184,32 +204,51 @@ private ISPU createSPU(SubscribeRequest req) { logger.debug("Add SPU to activation queue"); // Request SPU activation - if(!spuManager.activate(spu, req)) return null; + if (!spuManager.activate(spu, req)) + return null; return spu; } private void registerHandler(String masterSpuid, String spuid, EventHandler handler) { - logger.debug("Register SPU handler: " + spuid); - if (spuidPoll.get(masterSpuid) == null) - spuidPoll.put(masterSpuid, new ArrayList()); - - spuidPoll.get(masterSpuid).add(spuid); - handlers.put(spuid, handler); - sequenceNumbers.put(handler, 1); - fakeMap.put(spuid, masterSpuid); + synchronized (handlers) { + logger.debug("Register SPU handler: " + spuid); + + if (activeSpus.get(masterSpuid) == null) + activeSpus.put(masterSpuid, new ArrayList()); + + activeSpus.get(masterSpuid).add(spuid); + + handlers.put(spuid, handler); + + sequenceNumbers.put(spuid, 1); + spuids.put(spuid, masterSpuid); + } } - + private void unregisterHandler(String masterSpuid, String spuid) { - fakeMap.remove(spuid); - sequenceNumbers.remove(handlers.get(spuid)); - handlers.remove(spuid); - spuidPoll.get(masterSpuid).remove(spuid); + synchronized (handlers) { + logger.debug("Unregister SPU handler: " + spuid); + + spuids.remove(spuid); + sequenceNumbers.remove(spuid); + handlers.remove(spuid); + + // SPUids + activeSpus.get(masterSpuid).remove(spuid); + logger.debug(masterSpuid + " number of clients: " + activeSpus.get(masterSpuid).size()); + if (activeSpus.get(masterSpuid).isEmpty()) { + activeSpus.remove(masterSpuid); + + // Deactivate SPU + spuManager.deactivate(masterSpuid); + } + } } @Override - public long getRequests() { - return SubscribeProcessorBeans.getRequests(); + public long getUpdateRequests() { + return SubscribeProcessorBeans.getUpdateRequests(); } @Override @@ -232,16 +271,6 @@ public void reset() { SubscribeProcessorBeans.reset(); } - @Override - public void setKeepalive(int t) { - SubscribeProcessorBeans.setKeepalive(t); - } - - @Override - public int getKeepalive() { - return SubscribeProcessorBeans.getKeepalive(); - } - @Override public float getSPUs_time_min() { return SubscribeProcessorBeans.getSPUs_time_min(); @@ -276,4 +305,24 @@ public long getSPUProcessingTimeout() { public void setSPUProcessingTimeout(long t) { SubscribeProcessorBeans.setActiveSPUs(t); } + + @Override + public void scale_ms() { + SubscribeProcessorBeans.scale_ms(); + } + + @Override + public void scale_us() { + SubscribeProcessorBeans.scale_us(); + } + + @Override + public void scale_ns() { + SubscribeProcessorBeans.scale_ns(); + } + + @Override + public String getUnitScale() { + return SubscribeProcessorBeans.getUnitScale(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java index 1ef5f56f..7d3b0041 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java @@ -1,7 +1,7 @@ package it.unibo.arces.wot.sepa.engine.processing; public interface SubscribeProcessorMBean { - public long getRequests(); + public long getUpdateRequests(); public long getSubscribeRequests(); public long getUnsubscribeRequests(); @@ -15,9 +15,11 @@ public interface SubscribeProcessorMBean { public void reset(); - public void setKeepalive(int t); - public int getKeepalive(); - public long getSPUProcessingTimeout(); public void setSPUProcessingTimeout(long t); + + public void scale_ms(); + public void scale_us(); + public void scale_ns(); + public String getUnitScale(); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java new file mode 100644 index 00000000..ed4964ab --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java @@ -0,0 +1,66 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; +import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; + +public class UpdateProcessingThread extends Thread { + private UpdateProcessor updateProcessor; + private SchedulerRequestResponseQueue schedulerQueue; + private SubscribeProcessor subscribeProcessor; + + private LinkedBlockingQueue updateQueue = new LinkedBlockingQueue(); + + private final AtomicBoolean end = new AtomicBoolean(false); + + public UpdateProcessingThread(UpdateProcessor updateProcessor, SubscribeProcessor subscribeProcessor, + SchedulerRequestResponseQueue queue) { + this.updateProcessor = updateProcessor; + this.schedulerQueue = queue; + this.subscribeProcessor = subscribeProcessor; + + setName("SEPA-Update-Scheduler"); + } + + public void run() { + while (!end.get()) { + UpdateRequest request; + try { + request = updateQueue.take(); + } catch (InterruptedException e) { + return; + } + + // Process update request + Response ret = updateProcessor.process(request); + + // Notify update result + synchronized(schedulerQueue) { + if (schedulerQueue != null) schedulerQueue.addResponse(ret); + } + + // Subscription processing + if (ret.isUpdateResponse()) { + subscribeProcessor.process((UpdateResponse) ret); + } + } + } + + public void finish(){ + end.set(true); + } + + public void process(UpdateRequest req) { + updateQueue.add(req); + } + + public void setSchedulerQueue(SchedulerRequestResponseQueue queue) { + synchronized(schedulerQueue) { + schedulerQueue = queue; + } + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index d963772d..60c84d82 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -32,10 +32,11 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; -import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; +import it.unibo.arces.wot.sepa.engine.bean.UpdateProcessorBeans; import it.unibo.arces.wot.sepa.timing.Timings; -public class UpdateProcessor { +public class UpdateProcessor implements UpdateProcessorMBean { private static final Logger logger = LogManager.getLogger(); private SPARQL11Protocol endpoint; @@ -46,6 +47,8 @@ public UpdateProcessor(SPARQL11Properties properties, Semaphore endpointSemaphor endpoint = new SPARQL11Protocol(); this.endpointSemaphore = endpointSemaphore; this.properties = properties; + + SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); } public synchronized Response process(UpdateRequest req) { @@ -72,7 +75,7 @@ public synchronized Response process(UpdateRequest req) { Response ret; UpdateRequest request = new UpdateRequest(req.getToken(), properties.getUpdateMethod(), properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), - properties.getUpdatePath(), req.getSPARQL(), req.getTimeout(), req.getUsingGraphUri(), + properties.getUpdatePath(), req.getSPARQL(), UpdateProcessorBeans.getTimeout(), req.getUsingGraphUri(), req.getUsingNamedGraphUri(), authorizationHeader); logger.trace(request); ret = endpoint.update(request); @@ -81,10 +84,71 @@ public synchronized Response process(UpdateRequest req) { endpointSemaphore.release(); long stop = Timings.getTime(); + UpdateProcessorBeans.timings(start, stop); + logger.trace("Response: " + ret.toString()); Timings.log("UPDATE_PROCESSING_TIME", start, stop); - ProcessorBeans.updateTimings(start, stop); - + return ret; } + + @Override + public void reset() { + UpdateProcessorBeans.reset(); + } + + @Override + public long getRequests() { + return UpdateProcessorBeans.getRequests(); + } + + @Override + public float getTimingsCurrent() { + return UpdateProcessorBeans.getCurrent(); + } + + @Override + public float getTimingsMin() { + return UpdateProcessorBeans.getMin(); + } + + @Override + public float getTimingsAverage() { + return UpdateProcessorBeans.getAverage(); + } + + @Override + public float getTimingsMax() { + return UpdateProcessorBeans.getMax(); + } + + @Override + public long getTimeout() { + return UpdateProcessorBeans.getTimeout(); + } + + @Override + public void setTimeout(long t) { + UpdateProcessorBeans.setTimeout(t); + } + + @Override + public void scale_ms() { + UpdateProcessorBeans.scale_ms(); + } + + @Override + public void scale_us() { + UpdateProcessorBeans.scale_us(); + } + + @Override + public void scale_ns() { + UpdateProcessorBeans.scale_ns(); + } + + @Override + public String getUnitScale() { + return UpdateProcessorBeans.getUnitScale(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessorMBean.java new file mode 100644 index 00000000..fadbe77b --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessorMBean.java @@ -0,0 +1,20 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +public interface UpdateProcessorMBean { + public void reset(); + + public long getRequests(); + + public float getTimingsCurrent(); + public float getTimingsMin(); + public float getTimingsAverage(); + public float getTimingsMax(); + + public long getTimeout(); + public void setTimeout(long t); + + public void scale_ms(); + public void scale_us(); + public void scale_ns(); + public String getUnitScale(); +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index 0b4f21c6..d1f94605 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -123,7 +123,7 @@ public synchronized void unRegister(String spuID) { spuid2Request.remove(spuID); } - public synchronized boolean isValidSpuId(String id) { + private boolean isValidSpuId(String id) { return spus.containsKey(id); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java index c3dca3fc..caac019a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpGate.java @@ -1,8 +1,6 @@ package it.unibo.arces.wot.sepa.engine.protocol.http; import java.io.IOException; -import java.net.Inet4Address; -import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; import org.apache.http.ExceptionLogger; @@ -52,16 +50,6 @@ public HttpGate(EngineProperties properties, Scheduler scheduler) throws SEPAPro if(server.getEndpoint().getException()!=null) { throw new SEPAProtocolException(server.getEndpoint().getException()); } - - String address = server.getEndpoint().getAddress().toString(); - - try { - address = Inet4Address.getLocalHost().getHostAddress(); - } catch (UnknownHostException e1) { - throw new SEPAProtocolException(e1); - } - EngineBeans.setQueryURL("http://" + address + ":" + properties.getHttpPort()+properties.getQueryPath()); - EngineBeans.setUpdateURL("http://" + address + ":" + properties.getHttpPort()+properties.getUpdatePath()); System.out.println("SPARQL 1.1 Query | " + EngineBeans.getQueryURL()); System.out.println("SPARQL 1.1 Update | " + EngineBeans.getUpdateURL()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java index 60d40e55..3b2b7083 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java @@ -1,8 +1,6 @@ package it.unibo.arces.wot.sepa.engine.protocol.http; import java.io.IOException; -import java.net.Inet4Address; -import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeUnit; @@ -64,31 +62,10 @@ public HttpsGate(EngineProperties properties, Scheduler scheduler, Authorization throw new SEPAProtocolException(e); } - if (server.getEndpoint().getException() != null) { - throw new SEPAProtocolException(server.getEndpoint().getException()); - } - - String address = server.getEndpoint().getAddress().toString(); - try { - address = Inet4Address.getLocalHost().getHostAddress(); - } catch (UnknownHostException e1) { - throw new SEPAProtocolException(e1); - } - - EngineBeans.setSecureQueryURL("https://" + address + ":" + properties.getHttpsPort() - + properties.getSecurePath() + properties.getQueryPath()); - EngineBeans.setSecureUpdateURL("https://" + address + ":" + properties.getHttpsPort() - + properties.getSecurePath() + properties.getUpdatePath()); - EngineBeans.setRegistrationURL( - "https://" + address + ":" + properties.getHttpsPort() + properties.getRegisterPath()); - EngineBeans.setTokenRequestURL( - "https://" + address + ":" + properties.getHttpsPort() + properties.getTokenRequestPath()); - System.out.println("SPARQL 1.1 SE Query | " + EngineBeans.getSecureQueryURL()); System.out.println("SPARQL 1.1 SE Update | " + EngineBeans.getSecureUpdateURL()); System.out.println("Client registration | " + EngineBeans.getRegistrationURL()); System.out.println("Token request | " + EngineBeans.getTokenRequestURL()); - } public void shutdown() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java index f6cd5b43..f9fa1c1a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java @@ -1,17 +1,11 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; -import java.io.IOException; import java.net.URLDecoder; -import java.nio.charset.Charset; import java.util.Map; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpStatus; -import org.apache.http.ParseException; import org.apache.http.nio.protocol.HttpAsyncExchange; -import org.apache.http.util.EntityUtils; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -25,37 +19,6 @@ * This method parse the HTTP request according to * SPARQL 1.1 Protocol * - * * - * - *
    - *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    - *----------------------------------------------------------------------------------------------------------------------------------------
    - * query via GET              |   GET          query (exactly 1)                 None                                None
    - *                            |                default-graph-uri (0 or more)
    - *                            |                named-graph-uri (0 or more)
    - *----------------------------------------------------------------------------------------------------------------------------------------												
    - * query via URL-encoded POST |   POST         None                              application/x-www-form-urlencoded   URL-encoded, ampersand-separated query parameters.
    - *                            |                                                                                     query (exactly 1)
    - *                            |                                                                                     default-graph-uri (0 or more)
    - *                            |                                                                                     named-graph-uri (0 or more)
    - *----------------------------------------------------------------------------------------------------------------------------------------																													
    - * query via POST directly    |   POST         default-graph-uri (0 or more)
    - *                            |                named-graph-uri (0 or more)       application/sparql-query            Unencoded SPARQL query string
    - * 
    - * 2.1.4 Specifying an RDF Dataset
    - * 
    - * A SPARQL query is executed against an RDF Dataset. The RDF Dataset for a query may be specified either via the default-graph-uri and named-graph-uri parameters in the 
    - * SPARQL Protocol or in the SPARQL query string using the FROM and FROM NAMED keywords. 
    - * 
    - * If different RDF Datasets are specified in both the protocol request and the SPARQL query string, 
    - * then the SPARQL service must execute the query using the RDF Dataset given in the protocol request.
    - * 
    - * Note that a service may reject a query with HTTP response code 400 if the service does not allow protocol clients to specify the RDF Dataset.
    - * If an RDF Dataset is not specified in either the protocol request or the SPARQL query string, 
    - * then implementations may execute the query against an implementation-defined default RDF dataset.
    - * 
    - * - * * * @see QueryRequest * @see UpdateRequest @@ -71,6 +34,26 @@ public QueryHandler(Scheduler scheduler) throws IllegalArgumentException { protected Request parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolException { switch (exchange.getRequest().getRequestLine().getMethod().toUpperCase()) { case "GET": + /*
    +			 *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    +			 *----------------------------------------------------------------------------------------------------------------------------------------
    +			 * query via GET              |   GET          query (exactly 1)                 None                                None
    +			 *                            |                default-graph-uri (0 or more)
    +			 *                            |                named-graph-uri (0 or more)
    +			 * 
    +			 * 2.1.4 Specifying an RDF Dataset
    +			 * 
    +			 * A SPARQL query is executed against an RDF Dataset. The RDF Dataset for a query may be specified either via the default-graph-uri and named-graph-uri parameters in the 
    +			 * SPARQL Protocol or in the SPARQL query string using the FROM and FROM NAMED keywords. 
    +			 * 
    +			 * If different RDF Datasets are specified in both the protocol request and the SPARQL query string, 
    +			 * then the SPARQL service must execute the query using the RDF Dataset given in the protocol request.
    +			 * 
    +			 * Note that a service may reject a query with HTTP response code 400 if the service does not allow protocol clients to specify the RDF Dataset.
    +			 * If an RDF Dataset is not specified in either the protocol request or the SPARQL query string, 
    +			 * then implementations may execute the query against an implementation-defined default RDF dataset.
    +			 * 
    + * */ logger.debug("query via GET"); try { String requestUri = exchange.getRequest().getRequestLine().getUri(); @@ -97,47 +80,7 @@ protected Request parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolExcep throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage()); } case "POST": - try { - HttpEntity entity = ((HttpEntityEnclosingRequest) exchange.getRequest()).getEntity(); - String body = EntityUtils.toString(entity, Charset.forName("UTF-8")); - - Header[] headers = exchange.getRequest().getHeaders("Content-Type"); - if (headers.length != 1) { - logger.error("Content-Type is missing"); - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); - } - - if (headers[0].getValue().equals("application/sparql-query")) { - logger.trace("query via POST directly"); - - String requestUri = exchange.getRequest().getRequestLine().getUri(); - String graphUri = null; - String namedGraphUri = null; - - if (requestUri.indexOf('?') != -1) { - String queryParameters = requestUri.substring(requestUri.indexOf('?') + 1); - Map params = HttpUtilities.splitQuery(queryParameters); - graphUri = params.get("default-graph-uri"); - namedGraphUri = params.get("named-graph-uri"); - } - - return new QueryRequest(body, graphUri, namedGraphUri); - } else if (headers[0].getValue().equals("application/x-www-form-urlencoded")) { - String decodedBody = URLDecoder.decode(body, "UTF-8"); - Map params = HttpUtilities.splitQuery(decodedBody); - return new QueryRequest(params.get("query"), params.get("default-graph-uri"), - params.get("named-graph-uri")); - } - - logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, - "Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); - - } catch (ParseException | IOException e) { - logger.error(e.getMessage()); - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage()); - } - + return parsePost(exchange,"query"); } logger.error("UNSUPPORTED METHOD: " + exchange.getRequest().getRequestLine().getMethod().toUpperCase()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java index c81142c4..3091c8b4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java @@ -1,20 +1,30 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.util.Map; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpStatus; - +import org.apache.http.ParseException; import org.apache.http.nio.protocol.BasicAsyncRequestConsumer; import org.apache.http.nio.protocol.HttpAsyncExchange; import org.apache.http.nio.protocol.HttpAsyncRequestConsumer; import org.apache.http.nio.protocol.HttpAsyncRequestHandler; import org.apache.http.protocol.HttpContext; +import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.commons.request.QueryRequest; import it.unibo.arces.wot.sepa.commons.request.Request; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.engine.bean.HTTPHandlerBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.dependability.CORSManager; @@ -66,6 +76,99 @@ protected boolean corsHandling(HttpAsyncExchange exchange) { protected abstract Request parse(HttpAsyncExchange exchange); + /** + * SPARQL 1.1 Protocol + * + * * + * + *
    +	 *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    +	 *----------------------------------------------------------------------------------------------------------------------------------------
    +	 * update via URL-encoded POST|   POST         None                              application/x-www-form-urlencoded   URL-encoded, ampersand-separated query parameters.
    +	 *                            |                                                                                     update (exactly 1)
    +	 *                            |                                                                                     using-graph-uri (0 or more)
    +	 *                            |                                                                                     using-named-graph-uri (0 or more)
    +	 *----------------------------------------------------------------------------------------------------------------------------------------																													
    +	 * update via POST directly   |   POST        using-graph-uri (0 or more)        application/sparql-update           Unencoded SPARQL update request string
    +	 *                                            using-named-graph-uri (0 or more)
    +	 * ----------------------------------------------------------------------------------------------------------------------------------------												
    +	 * query via URL-encoded POST |   POST         None                              application/x-www-form-urlencoded   URL-encoded, ampersand-separated query parameters.
    +	 *                            |                                                                                     query (exactly 1)
    +	 *                            |                                                                                     default-graph-uri (0 or more)
    +	 *                            |                                                                                     named-graph-uri (0 or more)
    +	 *----------------------------------------------------------------------------------------------------------------------------------------																													
    +	 * query via POST directly    |   POST         default-graph-uri (0 or more)
    +	 *                            |                named-graph-uri (0 or more)       application/sparql-query            Unencoded SPARQL query string
    +	 * 
    + * + */ + protected Request parsePost(HttpAsyncExchange exchange, String type) { + String contentTypePost = "application/sparql-query"; + String defGraph = "default-graph-uri"; + String namedGraph = "named-graph-uri"; + if (type.equals("update")) { + contentTypePost = "application/sparql-update"; + defGraph = "using-graph-uri"; + namedGraph = "using-named-graph-uri"; + } + + String sparql = null; + String default_graph_uri = null; + String named_graph_uri = null; + + try { + HttpEntity entity = ((HttpEntityEnclosingRequest) exchange.getRequest()).getEntity(); + String body = EntityUtils.toString(entity, Charset.forName("UTF-8")); + + Header[] headers = exchange.getRequest().getHeaders("Content-Type"); + if (headers.length != 1) { + logger.error("Content-Type is missing"); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); + } + + if (headers[0].getValue().equals(contentTypePost)) { + logger.trace(type + " via POST directly"); + + String requestUri = exchange.getRequest().getRequestLine().getUri(); + String graphUri = null; + String namedGraphUri = null; + + if (requestUri.indexOf('?') != -1) { + String queryParameters = requestUri.substring(requestUri.indexOf('?') + 1); + Map params = HttpUtilities.splitQuery(queryParameters); + graphUri = params.get(defGraph); + namedGraphUri = params.get(namedGraph); + } + + sparql = body; + if (graphUri != null) default_graph_uri = URLDecoder.decode(graphUri,"UTF-8"); + if (namedGraphUri != null) named_graph_uri = URLDecoder.decode(namedGraphUri,"UTF-8"); + } else if (headers[0].getValue().equals("application/x-www-form-urlencoded")) { + logger.trace(type + " via URL ENCODED POST"); + + String decodedBody = URLDecoder.decode(body, "UTF-8"); + Map params = HttpUtilities.splitQuery(decodedBody); + + sparql = params.get(type); + default_graph_uri = params.get(defGraph); + named_graph_uri = params.get(namedGraph); + } else { + logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, + "Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + } + + } catch (ParseException | IOException e) { + logger.error(e.getMessage()); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage()); + } + + if (type.equals("query")) + return new QueryRequest(sparql, default_graph_uri, named_graph_uri); + else + return new UpdateRequest(sparql, default_graph_uri, named_graph_uri); + } + @Override public HttpAsyncRequestConsumer processRequest(HttpRequest request, HttpContext context) throws HttpException, IOException { @@ -88,8 +191,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont sepaRequest = parse(httpExchange); } catch (SPARQL11ProtocolException e) { logger.error("Parsing failed: " + httpExchange.getRequest()); - HttpUtilities.sendFailureResponse(httpExchange, e.getCode(), - "Parsing failed: " + e.getBody()); + HttpUtilities.sendFailureResponse(httpExchange, e.getCode(), "Parsing failed: " + e.getBody()); jmx.parsingFailed(); return; } @@ -111,7 +213,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont jmx.authorizingFailed(); return; } - + // Schedule request Timings.log(sepaRequest); scheduler.schedule(sepaRequest, new SPARQL11ResponseHandler(httpExchange, jmx)); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java index 419aca79..812b0820 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java @@ -1,45 +1,13 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.util.Map; - -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpEntityEnclosingRequest; - import org.apache.http.HttpStatus; -import org.apache.http.ParseException; import org.apache.http.nio.protocol.HttpAsyncExchange; -import org.apache.http.util.EntityUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; -/** - * SPARQL 1.1 Protocol - * - * * - * - *
    - *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    - *----------------------------------------------------------------------------------------------------------------------------------------
    - * update via URL-encoded POST|   POST         None                              application/x-www-form-urlencoded   URL-encoded, ampersand-separated query parameters.
    - *                            |                                                                                     update (exactly 1)
    - *                            |                                                                                     using-graph-uri (0 or more)
    - *                            |                                                                                     using-named-graph-uri (0 or more)
    - *----------------------------------------------------------------------------------------------------------------------------------------																													
    - * update via POST directly   |    POST       using-graph-uri (0 or more)       application/sparql-update           Unencoded SPARQL update request string
    - *                                            using-named-graph-uri (0 or more)
    - * 
    - * - */ public class UpdateHandler extends SPARQL11Handler { protected static final Logger logger = LogManager.getLogger(); @@ -50,69 +18,11 @@ public UpdateHandler(Scheduler scheduler) throws IllegalArgumentException { @Override protected Request parse(HttpAsyncExchange exchange) { if (!exchange.getRequest().getRequestLine().getMethod().toUpperCase().equals("POST")) { - logger.error("UNSUPPORTED METHOD: " + exchange.getRequest().getRequestLine().getMethod().toUpperCase()); - throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, - "Unsupported method: " + exchange.getRequest().getRequestLine().getMethod().toUpperCase()); - } - - String body = null; - HttpEntity entity = ((HttpEntityEnclosingRequest) exchange.getRequest()).getEntity(); - try { - body = EntityUtils.toString(entity, Charset.forName("UTF-8")); - } catch (ParseException | IOException e) { - body = e.getLocalizedMessage(); - } - - Header[] headers = exchange.getRequest().getHeaders("Content-Type"); - if (headers.length != 1) { - logger.error("Content-Type is missing"); - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); + logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, + "Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); } - - // Content-Type header can have parameters like charset - // i.e: Content-Type: text/html; charset=utf-8 - // Note: header.getValue() returns text/html; charset=utf-8 - // TODO: handle charset - String contentType = headers[0].getElements()[0].getName(); - - if (contentType.equals("application/sparql-update")) { - logger.debug("update via POST directly"); - - String usingGraphUri = null; - String usingNamedGraphUri = null; - - try { - String requestUri = exchange.getRequest().getRequestLine().getUri(); - if (requestUri.indexOf('?') != -1) { - String[] split = requestUri.split("\\?"); - if (split.length == 2) { - Map params = HttpUtilities.splitQuery(split[1]); - if (params.get("using-graph-uri") != null) usingGraphUri = URLDecoder.decode(params.get("using-graph-uri"), "UTF-8"); - if (params.get("using-named-graph-uri") != null) usingNamedGraphUri = URLDecoder.decode(params.get("using-named-graph-uri"), "UTF-8"); - } - } - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage()); - throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, e.getMessage()); - } - - return new UpdateRequest(body,usingGraphUri,usingNamedGraphUri); - } else if (contentType.equals("application/x-www-form-urlencoded")) { - try { - String decodedBody = URLDecoder.decode(body, "UTF-8"); - Map params = HttpUtilities.splitQuery(decodedBody); - logger.debug("update via URL ENCODED POST directly: "+params.get("update")); - - if (params.get("update") != null) return new UpdateRequest(params.get("update"),params.get("using-graph-uri"),params.get("using-named-graph-uri")); - - } catch (UnsupportedEncodingException e1) { - logger.error(e1.getMessage()); - throw new SPARQL11ProtocolException( HttpStatus.SC_BAD_REQUEST, e1.getMessage()); - } - } - - logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); - throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, - "Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + + return parsePost(exchange,"update"); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index b7d3c2b4..0ec83e80 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -133,10 +133,10 @@ private Response validateToken(String bearer) { String jwt = null; try { if (!bearer.startsWith("Bearer ")) - new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "authorization value MUST be of type Bearer"); + new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Authorization value MUST be of type Bearer"); jwt = bearer.substring(7); } catch (Exception e) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "authorization key value is wrong"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Authorization key value is wrong"); } // Token validation diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java index b672e309..d32daeb9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java @@ -1,7 +1,6 @@ package it.unibo.arces.wot.sepa.engine.protocol.websocket; import java.io.IOException; -import java.time.Instant; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -16,13 +15,14 @@ import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; +import it.unibo.arces.wot.sepa.timing.Timings; public class WebsocketEventHandler implements EventHandler { private static final Logger logger = LogManager.getLogger(); private WebSocket socket; private WebsocketBeans jmx; - private Instant start; + private long start; // Dependability manager private DependabilityManager dependabilityMng; @@ -43,23 +43,30 @@ private void send(Response ret) throws IOException { } public void startTiming() { - start = Instant.now(); + start = Timings.getTime(); } @Override public void sendResponse(Response response) throws IOException { long timing = 0; + + logger.trace(response); + if (response.isSubscribeResponse()) { timing = jmx.subscribeTimings(start); dependabilityMng.onSubscribe(socket, ((SubscribeResponse)response).getSpuid()); + logger.debug("Response #"+response.getToken()+" ("+timing+" ms)"); } else if (response.isUnsubscribeResponse()) { timing = jmx.unsubscribeTimings(start); dependabilityMng.onUnsubscribe(socket, ((UnsubscribeResponse)response).getSpuid()); + logger.debug("Response #"+response.getToken()+" ("+timing+" ms)"); } - - logger.debug("Response #"+response.getToken()+" ("+timing+" ms)"); - logger.trace(response); + else if (response.isError()) { + logger.debug("Response #"+response.getToken()); + logger.error(response); + } + send(response); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index a06cd386..b3d532d7 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -1,5 +1,6 @@ package it.unibo.arces.wot.sepa.engine.protocol.websocket; +import java.net.BindException; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.UnknownHostException; @@ -83,7 +84,7 @@ public WebsocketServer(int port, String path, Scheduler scheduler, Dependability @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { - logger.debug("@onOpen WebSocket: <" + conn + ">" + " Resource descriptor: " + conn.getResourceDescriptor()); + logger.debug("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); @@ -98,8 +99,7 @@ public void onOpen(WebSocket conn, ClientHandshake handshake) { @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { - logger.debug("@onClose WebSocket:<" + conn + "> Reason: <" + reason + "> Code: <" + code + "> Remote: <" - + remote + ">"); + logger.debug("@onClose: " + conn + " Reason: " + reason + " Code: " + code + " Remote: " + remote); if (!conn.getResourceDescriptor().equals(path)) return; @@ -129,9 +129,11 @@ public void onMessage(WebSocket conn, String message) { // Parse the request Request req = parseRequest(message, conn); - if (req == null) + if (req == null) { + logger.error("Failed to parse message: "+req); return; - + } + // Add active socket if (!activeSockets.containsKey(conn)) { activeSockets.put(conn, new WebsocketEventHandler(conn, jmx, dependabilityMng)); @@ -165,52 +167,61 @@ public void onMessage(WebSocket conn, String message) { protected Request parseRequest(String request, WebSocket conn) throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException { JsonObject req; + ErrorResponse error; try { req = new JsonParser().parse(request).getAsJsonObject(); - - if (req.has("subscribe")) { - String sparql = null; - String alias = null; - String defaultGraphUri = null; - String namedGraphUri = null; - - try { - sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); - } catch (Exception e) { - logger.error("SPARQL member not found"); - return null; - } - - try { - alias = req.get("subscribe").getAsJsonObject().get("alias").getAsString(); - } catch (Exception e) { - } - - try { - defaultGraphUri = req.get("subscribe").getAsJsonObject().get("default-graph-uri").getAsString(); - } catch (Exception e) { - } - - try { - namedGraphUri = req.get("subscribe").getAsJsonObject().get("named-graph-uri").getAsString(); - } catch (Exception e) { - } - - return new SubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri, null); - } else if (req.has("unsubscribe")) - return new UnsubscribeRequest(req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString()); - - } catch (Exception e) { - logger.debug(e.getLocalizedMessage()); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); - conn.send(response.toString()); + } catch (JsonParseException e) { + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException: " + request); + conn.send(error.toString()); return null; } - logger.debug("Bad request: " + request); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Bad request: " + request); - conn.send(response.toString()); + if (req.has("subscribe")) { + String sparql = null; + String alias = null; + String defaultGraphUri = null; + String namedGraphUri = null; + + try { + sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); + } catch (Exception e) { + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "sparql member not found: " + request); + conn.send(error.toString()); + return null; + } + + try { + alias = req.get("subscribe").getAsJsonObject().get("alias").getAsString(); + } catch (Exception e) { + } + + try { + defaultGraphUri = req.get("subscribe").getAsJsonObject().get("default-graph-uri").getAsString(); + } catch (Exception e) { + } + + try { + namedGraphUri = req.get("subscribe").getAsJsonObject().get("named-graph-uri").getAsString(); + } catch (Exception e) { + } + + return new SubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri, null); + } else if (req.has("unsubscribe")) { + String spuid; + try { + spuid = req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString(); + } catch (Exception e) { + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "spuid member not found: " + request); + conn.send(error.toString()); + return null; + } + + return new UnsubscribeRequest(spuid); + } + + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Bad request: " + request); + conn.send(error.toString()); return null; } @@ -246,7 +257,12 @@ public void onFragment(WebSocket conn, Framedata fragment) { @Override public void onError(WebSocket conn, Exception ex) { - logger.error("@onError WebSocket: <" + conn + "> Exception: " + ex); + logger.error("@onError: " + conn + " Exception: " + ex); + + if (ex.getClass().equals(BindException.class)) { + logger.fatal("Failed to start. Exit"); + System.exit(-1); + } if (!conn.getResourceDescriptor().equals(path)) return; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index b94e6dfb..0bc6145a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -73,12 +73,14 @@ public Scheduler(EngineProperties properties,SchedulerRequestResponseQueue queue this.setName("SEPA-Scheduler"); } - + public synchronized void schedule(Request request, ResponseHandler handler) { int token = getToken(); if (token == -1) { + SchedulerBeans.newRequest(request, false); try { - handler.sendResponse(new ErrorResponse(-1, 500, "Request refused: too many pending requests")); + logger.error("Request refused: too many pending requests: "+request); + if (handler != null) handler.sendResponse(new ErrorResponse(-1, 500, "Request refused: too many pending requests")); } catch (IOException e) { logger.error("Failed to send response on out of tokens"); } @@ -91,6 +93,8 @@ public synchronized void schedule(Request request, ResponseHandler handler) { queue.addRequest(new ScheduledRequest(token, request, handler)); Timings.log(request); + + SchedulerBeans.newRequest(request, true); } /** diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java index 54a89e6b..a08f206c 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java @@ -38,13 +38,13 @@ public void unRegisterTest(){ public void unRegisterInvalidID(){ spuManger.unRegister("pluto"); } - - @Test - public void isValidUIDTest(){ - Assert.assertFalse(spuManger.isValidSpuId("Cap")); - spuManger.register(new FakeSPU("Ironman"),null); - Assert.assertTrue(spuManger.isValidSpuId("Ironman")); - } +// +// @Test +// public void isValidUIDTest(){ +// Assert.assertFalse(spuManger.isValidSpuId("Cap")); +// spuManger.register(new FakeSPU("Ironman"),null); +// Assert.assertTrue(spuManger.isValidSpuId("Ironman")); +// } private class FakeSPU implements ISPU{ diff --git a/tools/arces-demo.jsap b/tools/arces-demo.jsap index deb67d4a..8eefa493 100644 --- a/tools/arces-demo.jsap +++ b/tools/arces-demo.jsap @@ -93,83 +93,6 @@ "comment": "emperature sensor used to show the need of a new air conditioning system in ST office", "label": "Temperature ST office" }, - "pepoli/6lowpan/network/NODO1/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura sala server Viale Pepoli (rete 6LowPan)", - "label": "Temperatura sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO1/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità sala server Viale Pepoli (rete 6LowPan)", - "label": "Umidità sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO1/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica sala server Viale Pepoli (rete 6LowPan)", - "label": "Pressione atmosferica sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO2/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura ufficio Luca Perilli (rete 6LowPan)", - "label": "Temperatura ufficio Luca Perilli" - }, - "pepoli/6lowpan/network/NODO2/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità ufficio SLuca Perilli (rete 6LowPan)", - "label": "Umidità ufficio Luca Perilli" - }, - "pepoli/6lowpan/network/NODO2/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica ufficio SLuca Perilli (rete 6LowPan)", - "label": "Pressione atmosferica sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO3/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura Blue Hall (rete 6LowPan)", - "label": "Temperatura Blue Hall" - }, - "pepoli/6lowpan/network/NODO3/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità Blue Hall (rete 6LowPan)", - "label": "Umidità Blue Hall" - }, - "pepoli/6lowpan/network/NODO3/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica Blue Hall (rete 6LowPan)", - "label": "Pressione atmosferica Blue Hall" - }, - "ground/lora/moisture/device1": { - "observation": "arces-monitor:ground-lora-moisture-device1", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità giardino 1 (rete LoRa)", - "label": "Umidità giardino" - }, - "ground/lora/moisture/device2": { - "observation": "arces-monitor:ground-lora-moisture-device2", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità giardino 2 (rete LoRa)", - "label": "Umidità giardino" - }, "arces/servers/ares/ercole/cpu/core-20/temperature": { "observation": "arces-monitor:ServerErcoleCore20", "unit": "qudt-unit-1-1:DegreeCelsius", diff --git a/tools/mqtt-topics.jsap b/tools/mqtt-topics.jsap new file mode 100644 index 00000000..6dd29a94 --- /dev/null +++ b/tools/mqtt-topics.jsap @@ -0,0 +1,392 @@ +{ + "ambiental": { + "/ffa574972ab9/applink/107/001BC50C700009CD": { + "observation": "arces-monitor:001BC50C700009CD-DASH7-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperature sensor used to show the need of a new air conditioning system in SEHM lab", + "label": "Temperature SEHM lab" + }, + "/ffa574972ab9/applink/107/001BC50C700009BB": { + "observation": "arces-monitor:001BC50C700009BB-DASH7-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "emperature sensor used to show the need of a new air conditioning system in ST office", + "label": "Temperature ST office" + }, + "5CCF7F15676D/temperature": { + "observation": "arces-monitor:5CCF7F15676D-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura zona rack sala server Toffano", + "label": "Temperatura zona rack" + }, + "5CCF7F15676D/humidity": { + "observation": "arces-monitor:5CCF7F15676D-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Mars", + "comment": "Umidità zona rack sala server Toffano", + "label": "Umidità zona rack" + }, + "5CCF7F1B599E/temperature": { + "observation": "arces-monitor:5CCF7F1B599E-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura zona finestra sala server Toffano", + "label": "Temperatura zona finestra" + }, + "5CCF7F1B599E/humidity": { + "observation": "arces-monitor:5CCF7F1B599E-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Mars", + "comment": "Umidità zona finestra sala server Toffano", + "label": "Umidità zona finestra" + }, + "5CCF7F151DC9/temperature": { + "observation": "arces-monitor:5CCF7F151DC9-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura esterna sala server Toffano", + "label": "Temperatura esterna" + }, + "5CCF7F1B58AC/temperature": { + "observation": "arces-monitor:5CCF7F1B58AC-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura zona rack sala server Pepoli", + "label": "Temperatura zona rack" + }, + "5CCF7F1B58AC/humidity": { + "observation": "arces-monitor:5CCF7F1B58AC-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità zona rack sala server Pepoli", + "label": "Umidità zona rack" + } + }, + "server": { + "samba": { + "arces/servers/mars/marsamba/hd/sda/temperature": { + "observation": "arces-monitor:ServerSambaHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server SAMBA HDD SDA", + "label": "Temperatura Server SAMBA HDD SDA" + }, + "arces/servers/mars/marsamba/hd/sdb/temperature": { + "observation": "arces-monitor:ServerSambaHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server SAMBA HDD SDB", + "label": "Temperatura Server SAMBA HDD SDB" + } + }, + "giove": { + "arces/servers/mars/giove/hd/sda/temperature": { + "observation": "arces-monitor:ServerGioveHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDA", + "label": "Temperatura Server GIOVE HDD SDA" + }, + "arces/servers/mars/giove/hd/sdb/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDB", + "label": "Temperatura Server GIOVE HDD SDB" + }, + "arces/servers/mars/giove/hd/sdc/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdc", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDC", + "label": "Temperatura Server GIOVE HDD SDC" + }, + "arces/servers/mars/giove/hd/sdd/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdd", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDD", + "label": "Temperatura Server GIOVE HDD SDD" + }, + "arces/servers/mars/giove/hd/sdf/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdf", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDF", + "label": "Temperatura Server GIOVE HDD SDF" + }, + "arces/servers/mars/giove/hd/sdg/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdg", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDG", + "label": "Temperatura Server GIOVE HDD SDG" + }, + "arces/servers/mars/giove/hd/sdh/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdh", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDH", + "label": "Temperatura Server GIOVE HDD SDH" + }, + "arces/servers/mars/giove/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerGiove6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 6", + "label": "Temperatura Server GIOVE Core 6" + }, + "arces/servers/mars/giove/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerGiove5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 5", + "label": "Temperatura Server GIOVE Core 5" + }, + "arces/servers/mars/giove/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerGiove4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 4", + "label": "Temperatura Server GIOVE Core 4" + }, + "arces/servers/mars/giove/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerGiove3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 3", + "label": "Temperatura Server GIOVE Core 3" + }, + "arces/servers/mars/giove/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerGiove2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 2", + "label": "Temperatura Server GIOVE Core 2" + }, + "arces/servers/mars/giove/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerGiove1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 1", + "label": "Temperatura Server GIOVE Core 1" + } + }, + "mml": { + "arces/servers/mars/mml/hd/sda/temperature": { + "observation": "arces-monitor:ServerMmlHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML HDD SDA", + "label": "Temperatura Server MML HDD SDA" + }, + "arces/servers/mars/mml/hd/sdb/temperature": { + "observation": "arces-monitor:ServerMmlHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML HDD SDB", + "label": "Temperatura Server MML HDD SDB" + }, + "arces/servers/mars/mml/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerMml6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 6", + "label": "Temperatura Server MML Core 6" + }, + "arces/servers/mars/mml/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerMml5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 5", + "label": "Temperatura Server MML Core 5" + }, + "arces/servers/mars/mml/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerMml4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 4", + "label": "Temperatura Server MML Core 4" + }, + "arces/servers/mars/mml/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerMml3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 3", + "label": "Temperatura Server MML Core 3" + }, + "arces/servers/mars/mml/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerMml2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 2", + "label": "Temperatura Server MML Core 2" + }, + "arces/servers/mars/mml/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerMml1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 1", + "label": "Temperatura Server MML Core 1" + } + }, + "ercole": { + "arces/servers/ares/ercole/cpu/core-20/temperature": { + "observation": "arces-monitor:ServerErcoleCore20", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 20", + "label": "Temperatura Server ERCOLE Core 20" + }, + "arces/servers/ares/ercole/cpu/core-19/temperature": { + "observation": "arces-monitor:ServerErcoleCore19", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 19", + "label": "Temperatura Server ERCOLE Core 19" + }, + "arces/servers/ares/ercole/cpu/core-18/temperature": { + "observation": "arces-monitor:ServerErcoleCore18", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 18", + "label": "Temperatura Server ERCOLE Core 18" + }, + "arces/servers/ares/ercole/cpu/core-17/temperature": { + "observation": "arces-monitor:ServerErcoleCore17", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 17", + "label": "Temperatura Server ERCOLE Core 17" + }, + "arces/servers/ares/ercole/cpu/core-16/temperature": { + "observation": "arces-monitor:ServerErcoleCore16", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 16", + "label": "Temperatura Server ERCOLE Core 16" + }, + "arces/servers/ares/ercole/cpu/core-15/temperature": { + "observation": "arces-monitor:ServerErcoleCore15", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 15", + "label": "Temperatura Server ERCOLE Core 15" + }, + "arces/servers/ares/ercole/cpu/core-14/temperature": { + "observation": "arces-monitor:ServerErcoleCore14", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 14", + "label": "Temperatura Server ERCOLE Core 14" + }, + "arces/servers/ares/ercole/cpu/core-13/temperature": { + "observation": "arces-monitor:ServerErcoleCore13", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 13", + "label": "Temperatura Server ERCOLE Core 13" + }, + "arces/servers/ares/ercole/cpu/core-12/temperature": { + "observation": "arces-monitor:ServerErcoleCore12", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 12", + "label": "Temperatura Server ERCOLE Core 12" + }, + "arces/servers/ares/ercole/cpu/core-11/temperature": { + "observation": "arces-monitor:ServerErcoleCore11", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 11", + "label": "Temperatura Server ERCOLE Core 11" + }, + "arces/servers/ares/ercole/cpu/core-10/temperature": { + "observation": "arces-monitor:ServerErcoleCore10", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 10", + "label": "Temperatura Server ERCOLE Core 10" + }, + "arces/servers/ares/ercole/cpu/core-9/temperature": { + "observation": "arces-monitor:ServerErcoleCore9", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 9", + "label": "Temperatura Server ERCOLE Core 9" + }, + "arces/servers/ares/ercole/cpu/core-8/temperature": { + "observation": "arces-monitor:ServerErcoleCore8", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 8", + "label": "Temperatura Server ERCOLE Core 8" + }, + "arces/servers/ares/ercole/cpu/core-7/temperature": { + "observation": "arces-monitor:ServerErcoleCore7", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 7", + "label": "Temperatura Server ERCOLE Core 7" + }, + "arces/servers/ares/ercole/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerErcoleCore6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 6", + "label": "Temperatura Server ERCOLE Core 6" + }, + "arces/servers/ares/ercole/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerErcoleCore5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 5", + "label": "Temperatura Server ERCOLE Core 5" + }, + "arces/servers/ares/ercole/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerErcoleCore4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 4", + "label": "Temperatura Server ERCOLE Core 4" + }, + "arces/servers/ares/ercole/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerErcoleCore3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 3", + "label": "Temperatura Server ERCOLE Core 3" + }, + "arces/servers/ares/ercole/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerErcoleCore2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 2", + "label": "Temperatura Server ERCOLE Core 2" + }, + "arces/servers/ares/ercole/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerErcoleCore1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 1", + "label": "Temperatura Server ERCOLE Core 1" + }, + "arces/servers/ares/ercole/hd/sda/temperature": { + "observation": "arces-monitor:ServerErcoleHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE HDD SDA", + "label": "Temperatura Server ERCOLE HDD SDA" + }, + "arces/servers/ares/ercole/hd/sdb/temperature": { + "observation": "arces-monitor:ServerErcoleHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE HDD SDB", + "label": "Temperatura Server ERCOLE HDD SDB" + } + } + } +} \ No newline at end of file diff --git a/tools/mqtt.jsap b/tools/mqtt.jsap deleted file mode 100644 index 9a0de1de..00000000 --- a/tools/mqtt.jsap +++ /dev/null @@ -1,766 +0,0 @@ -{ - "host": "localhost", - "authentication": { - "register": "https://mml.arces.unibo.it:8443/oauth/register", - "tokenRequest": "https://mml.arces.unibo.it:8443/oauth/token", - "client_id": "fRgQh7QdLiizbKFIrTb6P046B93/fLjVtLMlExudQ5GP7ttUZM6CwjIM9tNxj7bo", - "client_secret": "8Qlmv9FbGrOSXa5xTRM5DtdQ5F5fSFSzIodsKW18pKhUP3x/273lwQEI7mVMLR7N", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfsYbUe8zPGhDydK18+EuD/ZK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmvOxGe15Rt2UMtkgobdR/acMbMUPrGm1W5ReQvh7ObW4rEflIq6iYoYUkTPdNsBGvScA+twbDfmYi9REI2hcVMaWX6TJvHTkTsh8QBv9Q2h0TTpVb1Hv7cPOW/H/InWmW+RWg08Pp2ZbJWTZqbeFRaIL2FKqL/2kn2YqG0fH6sfx6Gb7dRZSklPz7/TFXTWOxeHAP97lfC1rGdYwmJtLRf7IML+ofs0aE0tntaZcRM1bD+GR+jy+cZbBbID9MfHlQSejuZcXewR0/E5W/JZ7L/hYZz2SDTlFgc6srax+1BcCq69asgMKq2WqLsSmzC3fySfMMF3888wrPk7J0kX0CmBTF2IvrMw8++nnuBBpf29DQtmZZY+d1kEDZG12CWv2YZr+T0PhU4UzezN+nV67nr55QqJzmgcM+r8DsYs/AVO7Y4qtMNHS+GB8TnC4Y72Z7O/FIzBWLsvH+8oCAlqIB48y60e2nH0O9t7pquA7XfX6rd1jW0boXlSNPv16/hHT/CDDl1slD0x+Z6jnfLU9YwYHANYAgWxHvz5Sa4rAYU4ZBhLi+pmBdsYsAdHey3Pz4wnxnAYdXM89CaJ07uJL1qqPlNWNiKfsCy1tjI6178zabS7bIhP3NWjSI+BWkVV2T2GPXpxX6F9q/pkyslhKYE", - "expires": "a0vLmKC0v7lBxET2tFKBHw==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - }, - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/secure/subscribe" - } - } - }, - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", - "using-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt" - }, - "namespaces": { - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "sosa": "http://www.w3.org/ns/sosa/", - "qudt-1-1": "http://qudt.org/1.1/schema/qudt#", - "qudt-unit-1-1": "http://qudt.org/1.1/vocab/unit#", - "arces-monitor": "http://wot.arces.unibo.it/monitor#", - "mqtt": "http://wot.arces.unibo.it/mqtt#", - "time": "http://www.w3.org/2006/time#", - "wgs84_pos": "http://www.w3.org/2003/01/geo/wgs84_pos#", - "gn": "http://www.geonames.org/ontology#" - }, - "extended": { - "simulate": false, - "mqtt": { - "url": "giove.arces.unibo.it", - "port": 52877, - "topics": [ - "#" - ], - "ssl": false - }, - "regexTopics": { - "pepoli:6lowpan:network": [ - ".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n", - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n", - ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n" - ] - }, - "jsonTopics": { - "ground/lora/moisture": { - "id": "nodeId", - "value": "moistureValue" - } - }, - "semantic-mappings": { - "/ffa574972ab9/applink/107/001BC50C700009CD": { - "observation": "arces-monitor:001BC50C700009CD-DASH7-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura sensori DASH7 sede Pepoli", - "label": "Temperatura sensore 001BC50C700009CD" - }, - "/ffa574972ab9/applink/107/001BC50C700009BB": { - "observation": "arces-monitor:001BC50C700009BB-DASH7-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura sensori DASH7 sede Pepoli", - "label": "Temperatura sensore 001BC50C700009BB" - }, - "pepoli/6lowpan/network/NODO1/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura sala server Viale Pepoli (rete 6LowPan)", - "label": "Temperatura sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO1/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità sala server Viale Pepoli (rete 6LowPan)", - "label": "Umidità sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO1/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo1-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica sala server Viale Pepoli (rete 6LowPan)", - "label": "Pressione atmosferica sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO2/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura ufficio Luca Perilli (rete 6LowPan)", - "label": "Temperatura ufficio Luca Perilli" - }, - "pepoli/6lowpan/network/NODO2/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità ufficio SLuca Perilli (rete 6LowPan)", - "label": "Umidità ufficio Luca Perilli" - }, - "pepoli/6lowpan/network/NODO2/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo2-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica ufficio SLuca Perilli (rete 6LowPan)", - "label": "Pressione atmosferica sala server Viale Pepoli" - }, - "pepoli/6lowpan/network/NODO3/Temperature": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura Blue Hall (rete 6LowPan)", - "label": "Temperatura Blue Hall" - }, - "pepoli/6lowpan/network/NODO3/Humidity": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità Blue Hall (rete 6LowPan)", - "label": "Umidità Blue Hall" - }, - "pepoli/6lowpan/network/NODO3/Pressure": { - "observation": "arces-monitor:Pepoli-6lowpan-Nodo3-Pressure", - "unit": "qudt-unit-1-1:Millibar", - "location": "arces-monitor:Star", - "comment": "Pressione atmosferica Blue Hall (rete 6LowPan)", - "label": "Pressione atmosferica Blue Hall" - }, - "ground/lora/moisture/device1": { - "observation": "arces-monitor:ground-lora-moisture-device1", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità giardino 1 (rete LoRa)", - "label": "Umidità giardino" - }, - "ground/lora/moisture/device2": { - "observation": "arces-monitor:ground-lora-moisture-device2", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità giardino 2 (rete LoRa)", - "label": "Umidità giardino" - }, - "arces/servers/ares/ercole/cpu/core-20/temperature": { - "observation": "arces-monitor:ServerErcoleCore20", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 20", - "label": "Temperatura Server ERCOLE Core 20" - }, - "arces/servers/ares/ercole/cpu/core-19/temperature": { - "observation": "arces-monitor:ServerErcoleCore19", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 19", - "label": "Temperatura Server ERCOLE Core 19" - }, - "arces/servers/ares/ercole/cpu/core-18/temperature": { - "observation": "arces-monitor:ServerErcoleCore18", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 18", - "label": "Temperatura Server ERCOLE Core 18" - }, - "arces/servers/ares/ercole/cpu/core-17/temperature": { - "observation": "arces-monitor:ServerErcoleCore17", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 17", - "label": "Temperatura Server ERCOLE Core 17" - }, - "arces/servers/ares/ercole/cpu/core-16/temperature": { - "observation": "arces-monitor:ServerErcoleCore16", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 16", - "label": "Temperatura Server ERCOLE Core 16" - }, - "arces/servers/ares/ercole/cpu/core-15/temperature": { - "observation": "arces-monitor:ServerErcoleCore15", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 15", - "label": "Temperatura Server ERCOLE Core 15" - }, - "arces/servers/ares/ercole/cpu/core-14/temperature": { - "observation": "arces-monitor:ServerErcoleCore14", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 14", - "label": "Temperatura Server ERCOLE Core 14" - }, - "arces/servers/ares/ercole/cpu/core-13/temperature": { - "observation": "arces-monitor:ServerErcoleCore13", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 13", - "label": "Temperatura Server ERCOLE Core 13" - }, - "arces/servers/ares/ercole/cpu/core-12/temperature": { - "observation": "arces-monitor:ServerErcoleCore12", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 12", - "label": "Temperatura Server ERCOLE Core 12" - }, - "arces/servers/ares/ercole/cpu/core-11/temperature": { - "observation": "arces-monitor:ServerErcoleCore11", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 11", - "label": "Temperatura Server ERCOLE Core 11" - }, - "arces/servers/ares/ercole/cpu/core-10/temperature": { - "observation": "arces-monitor:ServerErcoleCore10", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 10", - "label": "Temperatura Server ERCOLE Core 10" - }, - "arces/servers/ares/ercole/cpu/core-9/temperature": { - "observation": "arces-monitor:ServerErcoleCore9", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 9", - "label": "Temperatura Server ERCOLE Core 9" - }, - "arces/servers/ares/ercole/cpu/core-8/temperature": { - "observation": "arces-monitor:ServerErcoleCore8", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 8", - "label": "Temperatura Server ERCOLE Core 8" - }, - "arces/servers/ares/ercole/cpu/core-7/temperature": { - "observation": "arces-monitor:ServerErcoleCore7", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 7", - "label": "Temperatura Server ERCOLE Core 7" - }, - "arces/servers/ares/ercole/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerErcoleCore6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 6", - "label": "Temperatura Server ERCOLE Core 6" - }, - "arces/servers/ares/ercole/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerErcoleCore5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 5", - "label": "Temperatura Server ERCOLE Core 5" - }, - "arces/servers/ares/ercole/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerErcoleCore4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 4", - "label": "Temperatura Server ERCOLE Core 4" - }, - "arces/servers/ares/ercole/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerErcoleCore3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 3", - "label": "Temperatura Server ERCOLE Core 3" - }, - "arces/servers/ares/ercole/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerErcoleCore2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 2", - "label": "Temperatura Server ERCOLE Core 2" - }, - "arces/servers/ares/ercole/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerErcoleCore1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 1", - "label": "Temperatura Server ERCOLE Core 1" - }, - "arces/servers/mars/mml/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerMml6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 6", - "label": "Temperatura Server MML Core 6" - }, - "arces/servers/mars/mml/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerMml5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 5", - "label": "Temperatura Server MML Core 5" - }, - "arces/servers/mars/mml/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerMml4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 4", - "label": "Temperatura Server MML Core 4" - }, - "arces/servers/mars/mml/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerMml3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 3", - "label": "Temperatura Server MML Core 3" - }, - "arces/servers/mars/mml/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerMml2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 2", - "label": "Temperatura Server MML Core 2" - }, - "arces/servers/mars/mml/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerMml1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 1", - "label": "Temperatura Server MML Core 1" - }, - "arces/servers/mars/giove/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerGiove6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 6", - "label": "Temperatura Server GIOVE Core 6" - }, - "arces/servers/mars/giove/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerGiove5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 5", - "label": "Temperatura Server GIOVE Core 5" - }, - "arces/servers/mars/giove/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerGiove4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 4", - "label": "Temperatura Server GIOVE Core 4" - }, - "arces/servers/mars/giove/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerGiove3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 3", - "label": "Temperatura Server GIOVE Core 3" - }, - "arces/servers/mars/giove/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerGiove2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 2", - "label": "Temperatura Server GIOVE Core 2" - }, - "arces/servers/mars/giove/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerGiove1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 1", - "label": "Temperatura Server GIOVE Core 1" - }, - "arces/servers/mars/mml/hd/sda/temperature": { - "observation": "arces-monitor:ServerMmlHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML HDD SDA", - "label": "Temperatura Server MML HDD SDA" - }, - "arces/servers/mars/mml/hd/sdb/temperature": { - "observation": "arces-monitor:ServerMmlHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML HDD SDB", - "label": "Temperatura Server MML HDD SDB" - }, - "arces/servers/mars/giove/hd/sda/temperature": { - "observation": "arces-monitor:ServerGioveHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDA", - "label": "Temperatura Server GIOVE HDD SDA" - }, - "arces/servers/mars/giove/hd/sdb/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDB", - "label": "Temperatura Server GIOVE HDD SDB" - }, - "arces/servers/mars/giove/hd/sdc/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdc", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDC", - "label": "Temperatura Server GIOVE HDD SDC" - }, - "arces/servers/mars/giove/hd/sdd/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdd", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDD", - "label": "Temperatura Server GIOVE HDD SDD" - }, - "arces/servers/mars/giove/hd/sdf/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdf", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDF", - "label": "Temperatura Server GIOVE HDD SDF" - }, - "arces/servers/mars/giove/hd/sdg/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdg", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDG", - "label": "Temperatura Server GIOVE HDD SDG" - }, - "arces/servers/mars/giove/hd/sdh/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdh", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDH", - "label": "Temperatura Server GIOVE HDD SDH" - }, - "arces/servers/mars/marsamba/hd/sda/temperature": { - "observation": "arces-monitor:ServerSambaHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server SAMBA HDD SDA", - "label": "Temperatura Server SAMBA HDD SDA" - }, - "arces/servers/mars/marsamba/hd/sdb/temperature": { - "observation": "arces-monitor:ServerSambaHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server SAMBA HDD SDB", - "label": "Temperatura Server SAMBA HDD SDB" - }, - "arces/servers/ares/ercole/hd/sda/temperature": { - "observation": "arces-monitor:ServerErcoleHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE HDD SDA", - "label": "Temperatura Server ERCOLE HDD SDA" - }, - "arces/servers/ares/ercole/hd/sdb/temperature": { - "observation": "arces-monitor:ServerErcoleHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE HDD SDB", - "label": "Temperatura Server ERCOLE HDD SDB" - }, - "5CCF7F15676D/temperature": { - "observation": "arces-monitor:5CCF7F15676D-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura zona rack sala server Toffano", - "label": "Temperatura zona rack" - }, - "5CCF7F15676D/humidity": { - "observation": "arces-monitor:5CCF7F15676D-humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Mars", - "comment": "Umidità zona rack sala server Toffano", - "label": "Umidità zona rack" - }, - "5CCF7F1B599E/temperature": { - "observation": "arces-monitor:5CCF7F1B599E-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura zona finestra sala server Toffano", - "label": "Temperatura zona finestra" - }, - "5CCF7F1B599E/humidity": { - "observation": "arces-monitor:5CCF7F1B599E-humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Mars", - "comment": "Umidità zona finestra sala server Toffano", - "label": "Umidità zona finestra" - }, - "5CCF7F151DC9/temperature": { - "observation": "arces-monitor:5CCF7F151DC9-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura esterna sala server Toffano", - "label": "Temperatura esterna" - }, - "5CCF7F1B58AC/temperature": { - "observation": "arces-monitor:5CCF7F1B58AC-temperature", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Star", - "comment": "Temperatura zona rack sala server Pepoli", - "label": "Temperatura zona rack" - }, - "5CCF7F1B58AC/humidity": { - "observation": "arces-monitor:5CCF7F1B58AC-humidity", - "unit": "qudt-unit-1-1:Percent", - "location": "arces-monitor:Star", - "comment": "Umidità zona rack sala server Pepoli", - "label": "Umidità zona rack" - } - } - }, - "updates": { - "REMOVE_PLACE": { - "sparql": "DELETE WHERE {?place ?s ?p}", - "forcedBindings": { - "place": { - "type": "uri", - "value": "arces-monitor:Mars" - } - }, - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "ADD_PLACE": { - "sparql": "INSERT DATA {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}", - "forcedBindings": { - "place": { - "type": "uri", - "value": "arces-monitor:Mars" - }, - "name": { - "type": "literal", - "value": "Mars" - }, - "lat": { - "type": "literal", - "value": "44.489664", - "datatype": "xsd:decimal" - }, - "long": { - "type": "literal", - "value": "11.357023", - "datatype": "xsd:decimal" - } - }, - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "MQTT_MESSAGE": { - "sparql": "DELETE {?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} INSERT {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?timestamp} WHERE {OPTIONAL{?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} . BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Message-\",STRUUID())) AS ?message) . BIND(now() AS ?timestamp)}", - "forcedBindings": { - "value": { - "type": "literal", - "value": "mqttValueXYZ" - }, - "topic": { - "type": "literal", - "value": "mqttTopicXYZ" - }, - "broker": { - "type": "uri", - "value": "tcp://giove.arces.unibo.it:52887" - } - } - }, - "LOG_QUANTITY": { - "sparql": "INSERT {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value; time:inXSDDateTimeStamp ?timestamp} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#Log-\",STRUUID())) AS ?log) . BIND(now() AS ?timestamp)}", - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/monitor/log", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/log" - }, - "forcedBindings": { - "quantity": { - "type": "uri", - "value": "arces-monitor:QuantityValueXYZ" - }, - "value": { - "type": "literal", - "datatype": "xsd:decimal", - "value": "123.456" - } - } - }, - "REMOVE_OBSERVATION": { - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - }, - "sparql": "DELETE {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity. ?quantity qudt-1-1:numericValue ?value};DELETE WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "arces-monitor:ObservationXYZ" - } - } - }, - "ADD_OBSERVATION": { - "sparql": "DELETE {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity. ?quantity qudt-1-1:numericValue ?value}; DELETE {?observation rdf:type sosa:Observation ; rdfs:label ?X ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantityOLD . ?quantityOLD rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unitOLD} WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?labelOLD ; sosa:hasFeatureOfInterest ?locationOLD ; sosa:hasResult ?quantityOLD . ?quantityOLD rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unitOLD};INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity)}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "arces-monitor:ObservationXYZ" - }, - "comment": { - "type": "literal", - "value": "This is an observation" - }, - "label": { - "type": "literal", - "value": "The observation XYZ" - }, - "location": { - "type": "uri", - "value": "arces-monitor:Mars" - }, - "topic": { - "type": "literal", - "value": "mqttTopicXYZ" - }, - "unit": { - "type": "uri", - "value": "qudt-unit-1-1:DegreeCelsius" - } - }, - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "UPDATE_OBSERVATION_VALUE": { - "sparql": "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "arces-monitor:ObservationXYZ" - }, - "value": { - "type": "literal", - "datatype": "xsd:decimal", - "value": "12345.67890" - } - }, - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - } - }, - "queries": { - "LOG_QUANTITY": { - "sparql": "SELECT * WHERE {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value; time:inXSDDateTimeStamp ?timestamp}", - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/log", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/log" - } - }, - "PLACES": { - "sparql": "SELECT * WHERE {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}", - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "OBSERVATIONS_TOPICS": { - "sparql": "SELECT ?observation ?topic WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}", - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "OBSERVATIONS": { - "sparql": "SELECT ?observation ?location ?label ?quantity ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "OBSERVATIONS_BY_LOCATION": { - "sparql": "SELECT ?label ?value ?unit WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings": { - "location": { - "type": "uri", - "value": "arces-monitor:Mars" - } - }, - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "OBSERVATIONS_BY_UNIT": { - "sparql": "SELECT ?location ?label ?value WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings": { - "unit": { - "type": "uri", - "value": "qudt-unit-1-1:DegreeCelsius" - } - }, - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "ALL_VALUES": { - "sparql": "SELECT ?location ?label ?value ?unit WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", - "forcedBindings": { - "observation": { - "type": "uri", - "value": "arces-monitor:ObservationXYZ" - } - }, - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/monitor/live", - "named-graph-uri": "http://wot.arces.unibo.it/monitor/live" - } - }, - "MQTT_TOPICS_COUNT": { - "sparql": "SELECT (COUNT(DISTINCT ?topic) AS ?topics) WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic}" - }, - "MQTT_TOPICS": { - "sparql": "SELECT DISTINCT ?broker ?topic WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" - }, - "MQTT_TOPIC_VALUE": { - "sparql": "SELECT ?value WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", - "forcedBindings": { - "topic": { - "type": "literal", - "value": "mqttTopicXYZ" - } - } - }, - "MQTT_MESSAGES": { - "sparql": "SELECT ?broker ?topic ?value WHERE {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" - } - } -} \ No newline at end of file diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index c62cf901..e25b2174 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -89,6 +89,7 @@ import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; @@ -127,7 +128,8 @@ public class Dashboard { private DashboardHandler handler = new DashboardHandler(); private JSAP appProfile; private Properties appProperties = new Properties(); - + private AuthenticationProperties oauth = null; + private DefaultTableModel namespacesDM; private String namespacesHeader[] = new String[] { "Prefix", "URI" }; @@ -148,6 +150,51 @@ public class Dashboard { private JTextArea updateSPARQL; private JTextArea querySPARQL; + private DefaultTableModel propertiesDM; + private String propertiesHeader[] = new String[] { "Property", "Domain", "Range", "Comment" }; + + private JFrame frmSepaDashboard; + + private Panel sparqlTab; + + private JTable namespacesTable; + private JTable bindingsResultsTable; + private JTable updateForcedBindings; + private JTable queryForcedBindings; + private JLabel updateURL; + private JLabel usingGraphURI; + private JLabel usingNamedGraphURI; + private JLabel defaultGraphURI; + private JLabel namedGraphURI; + private JLabel subscribeURL; + private JLabel queryURL; + + private JButton updateButton; + private JButton subscribeButton; + + private String updateID; + private String queryID; + + private JList queryList; + private JList updateList; + private JTabbedPane mainTabs; + private JTextField timeout; + + private JTextArea textArea; + + private String jksName = "sepa.jks"; + private String jksPass = "sepa2017"; + private String keyPass = "sepa2017"; + + private JTextField userID; + private JButton btnQuery; + private JLabel updateInfo; + private JLabel queryInfo; + + private JButton btnRegister; + + private SEPASecurityManager sm; + class DashboardHandler implements ISubscriptionHandler { @Override public void onSemanticEvent(Notification n) { @@ -164,9 +211,6 @@ public void onSemanticEvent(Notification n) { if (notify.getRemovedBindings() != null) removed = notify.getRemovedBindings().size(); - // if (chckbxClearonnotify.isSelected()) - // subscriptionResultsDM.get(spuid).clear(); - subscriptionResultsDM.get(spuid).setResults(notify, spuid); subscriptionResultsLabels.get(spuid) @@ -220,47 +264,6 @@ public void clear() { } } - private DefaultTableModel propertiesDM; - private String propertiesHeader[] = new String[] { "Property", "Domain", "Range", "Comment" }; - - private JFrame frmSepaDashboard; - - private Panel sparqlTab; - - private JTable namespacesTable; - private JTable bindingsResultsTable; - private JTable updateForcedBindings; - private JTable queryForcedBindings; - private JLabel updateURL; - private JLabel usingGraphURI; - private JLabel usingNamedGraphURI; - private JLabel defaultGraphURI; - private JLabel namedGraphURI; - private JLabel subscribeURL; - private JLabel queryURL; - - private JButton updateButton; - private JButton queryButton; - private JButton subscribeButton; - - private String updateID; - private String queryID; - - private JList queryList; - private JList updateList; - private JTabbedPane mainTabs; - private JTextField updateTimeout; - private JTextField queryTimeout; - - private JTextArea textArea; - - private String jksName = "sepa.jks"; - - private String jksPass = "sepa2017"; - - private String keyPass = "sepa2017"; - private JTextField userID; - private class CopyAction extends AbstractAction { /** @@ -342,10 +345,6 @@ public Object getValueAt(int rowIndex, int columnIndex) { @Override public Class getColumnClass(int columnIndex) { - /* - * if (columnIndex == 0 || columnIndex == 1) return String.class; return - * Boolean.class; - */ return String.class; } @@ -434,10 +433,6 @@ public void setResults(ARBindingsResults res, String spuid) { if (res == null) return; - // Date date = new Date(); - // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - // String timestamp = sdf.format(date); - ArrayList vars = res.getAddedBindings().getVariables(); for (String var : res.getRemovedBindings().getVariables()) { if (!vars.contains(var)) @@ -456,7 +451,6 @@ public void setResults(ARBindingsResults res, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getValue(var), sol.isLiteral(var), false)); } - // row.put("", new BindingValue(timestamp, false, false)); rows.add(row); } } @@ -467,7 +461,6 @@ public void setResults(ARBindingsResults res, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getValue(var), sol.isLiteral(var), true)); } - // row.put("", new BindingValue(timestamp, false, true)); rows.add(row); } } @@ -475,15 +468,6 @@ public void setResults(ARBindingsResults res, String spuid) { subscriptionResultsTables.get(spuid).changeSelection(subscriptionResultsTables.get(spuid).getRowCount() - 1, 0, false, false); - // if (chckbxAutoscroll.isSelected()) - // if (spuid != null) - // subscriptionResultsTables.get(spuid) - // .changeSelection(subscriptionResultsTables.get(spuid).getRowCount() - 1, 0, - // false, false); - // else - // bindingsResultsTable.changeSelection(bindingsResultsTable.getRowCount() - 1, - // 0, false, false); - super.fireTableDataChanged(); } @@ -544,15 +528,10 @@ public void setAddedResults(BindingsResults bindingsResults, String spuid) { if (bindingsResults == null) return; - // Date date = new Date(); - // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - // String timestamp = sdf.format(date); - ArrayList vars = bindingsResults.getVariables(); if (!columns.containsAll(vars) || columns.size() != vars.size()) { columns.clear(); - // vars.add(""); columns.addAll(vars); super.fireTableStructureChanged(); } @@ -562,22 +541,12 @@ public void setAddedResults(BindingsResults bindingsResults, String spuid) { for (String var : sol.getVariables()) { row.put(var, new BindingValue(sol.getValue(var), sol.isLiteral(var), true)); } - // row.put("", new BindingValue(timestamp, false, true)); rows.add(row); } subscriptionResultsTables.get(spuid).changeSelection(subscriptionResultsTables.get(spuid).getRowCount() - 1, 0, false, false); - // if (chckbxAutoscroll.isSelected()) - // if (spuid != null) - // subscriptionResultsTables.get(spuid) - // .changeSelection(subscriptionResultsTables.get(spuid).getRowCount() - 1, 0, - // false, false); - // else - // bindingsResultsTable.changeSelection(bindingsResultsTable.getRowCount() - 1, - // 0, false, false); - super.fireTableDataChanged(); } } @@ -752,7 +721,6 @@ private boolean loadSAP(String file) { bindingsDM.clear(); updateButton.setEnabled(false); - queryButton.setEnabled(false); subscribeButton.setEnabled(false); if (file == null) { @@ -770,13 +738,10 @@ private boolean loadSAP(String file) { logger.error(e.getMessage()); return false; } - + // LOAD properties String path = appProperties.getProperty("appProfile"); -// jksName = appProperties.getProperty("jksName"); -// jksPass = appProperties.getProperty("jksPass"); -// keyPass = appProperties.getProperty("keyPass"); - + if (path == null) { logger.error("Path in dashboard.properties is null"); return false; @@ -811,18 +776,34 @@ private boolean loadSAP(String file) { queryListDM.add(subscribe); } - try { - SEPASecurityManager sm = new SEPASecurityManager(jksName , jksPass , keyPass ); - sepaClient = new GenericClient(appProfile, sm); -// Response ret = sm.register(appProfile.getAuthenticationProperties().getRegisterUrl(), "SEPATest"); -// if (ret.isRegistrationResponse()) { -// RegistrationResponse credentials = (RegistrationResponse) ret; -// appProfile.getAuthenticationProperties().setCredentials(credentials.getClientId(), -// credentials.getClientSecret()); -// } - } catch (SEPAProtocolException | SEPASecurityException e) { - logger.error(e.getMessage()); - System.exit(-1); + // Security + if (appProfile.isSecure()) { + try { + oauth = new AuthenticationProperties(appProfile.getFileName()); + } catch (SEPAPropertiesException | SEPASecurityException e1) { + logger.error(e1.getMessage()); + return false; + } + try { + sm = new SEPASecurityManager(jksName, jksPass, keyPass,oauth); + sepaClient = new GenericClient(appProfile, sm); + } catch (SEPAProtocolException | SEPASecurityException e) { + logger.error(e.getMessage()); + return false; + } + btnRegister.setEnabled(true); + userID.setEnabled(true); + } + else { + btnRegister.setEnabled(false); + userID.setEnabled(false); + + try { + sepaClient = new GenericClient(appProfile); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); + return false; + } } return true; @@ -840,6 +821,7 @@ public DashboardFileFilter(String title, String ext) { @Override public boolean accept(File f) { + if (f.isDirectory()) return true; for (String ext : extensions) if (f.getName().contains(ext)) return true; @@ -900,8 +882,8 @@ public boolean isCellEditable(int row, int column) { mainTabs.addTab("SPARQL", null, sparqlTab, null); mainTabs.setEnabledAt(0, true); GridBagLayout gbl_sparqlTab = new GridBagLayout(); - gbl_sparqlTab.columnWidths = new int[] { 457, 0, 0 }; - gbl_sparqlTab.rowHeights = new int[] { 0, 155, 82, 33, 172, 0 }; + gbl_sparqlTab.columnWidths = new int[] { 233, 0, 0 }; + gbl_sparqlTab.rowHeights = new int[] { 0, 155, 82, 47, 172, 0 }; gbl_sparqlTab.columnWeights = new double[] { 1.0, 1.0, Double.MIN_VALUE }; gbl_sparqlTab.rowWeights = new double[] { 0.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE }; sparqlTab.setLayout(gbl_sparqlTab); @@ -1073,7 +1055,7 @@ public boolean isCellEditable(int row, int column) { panel_2.setLayout(gbl_panel_2); JLabel label_12 = new JLabel("UPDATES"); - label_12.setForeground(UIManager.getColor("Desktop.background")); + label_12.setForeground(Color.BLACK); label_12.setFont(new Font("Lucida Grande", Font.BOLD, 14)); GridBagConstraints gbc_label_12 = new GridBagConstraints(); gbc_label_12.anchor = GridBagConstraints.NORTH; @@ -1109,7 +1091,7 @@ public void valueChanged(ListSelectionEvent e) { panel_3.setLayout(gbl_panel_3); JLabel lblForcedBindings = new JLabel("FORCED BINDINGS"); - lblForcedBindings.setForeground(UIManager.getColor("Desktop.background")); + lblForcedBindings.setForeground(Color.BLACK); GridBagConstraints gbc_lblForcedBindings = new GridBagConstraints(); gbc_lblForcedBindings.anchor = GridBagConstraints.NORTH; gbc_lblForcedBindings.insets = new Insets(0, 0, 5, 0); @@ -1160,7 +1142,7 @@ public void tableChanged(TableModelEvent e) { panel_4.setLayout(gbl_panel_4); JLabel label_14 = new JLabel("QUERIES"); - label_14.setForeground(UIManager.getColor("Desktop.background")); + label_14.setForeground(Color.BLACK); label_14.setFont(new Font("Lucida Grande", Font.BOLD, 14)); GridBagConstraints gbc_label_14 = new GridBagConstraints(); gbc_label_14.anchor = GridBagConstraints.NORTH; @@ -1196,7 +1178,7 @@ public void valueChanged(ListSelectionEvent e) { panel_5.setLayout(gbl_panel_5); JLabel lblForcedBindings_1 = new JLabel("FORCED BINDINGS"); - lblForcedBindings_1.setForeground(UIManager.getColor("Desktop.background")); + lblForcedBindings_1.setForeground(Color.BLACK); GridBagConstraints gbc_lblForcedBindings_1 = new GridBagConstraints(); gbc_lblForcedBindings_1.anchor = GridBagConstraints.NORTH; gbc_lblForcedBindings_1.insets = new Insets(0, 0, 5, 0); @@ -1258,9 +1240,9 @@ public void tableChanged(TableModelEvent e) { gbc_panel_6.gridy = 3; sparqlTab.add(panel_6, gbc_panel_6); GridBagLayout gbl_panel_6 = new GridBagLayout(); - gbl_panel_6.columnWidths = new int[] { 0, 93, 0, 0 }; + gbl_panel_6.columnWidths = new int[] { 0, 120, 57, 0, 0 }; gbl_panel_6.rowHeights = new int[] { 28, 0 }; - gbl_panel_6.columnWeights = new double[] { 0.0, 0.0, 1.0, Double.MIN_VALUE }; + gbl_panel_6.columnWeights = new double[] { 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE }; gbl_panel_6.rowWeights = new double[] { 0.0, Double.MIN_VALUE }; panel_6.setLayout(gbl_panel_6); @@ -1280,23 +1262,32 @@ public void actionPerformed(ActionEvent e) { } } }); - updateButton.setForeground(UIManager.getColor("Desktop.background")); + updateButton.setForeground(Color.BLACK); updateButton.setEnabled(false); - updateTimeout = new JTextField(); + updateInfo = new JLabel("---"); + GridBagConstraints gbc_udpdateInfo = new GridBagConstraints(); + gbc_udpdateInfo.fill = GridBagConstraints.VERTICAL; + gbc_udpdateInfo.insets = new Insets(0, 0, 0, 5); + gbc_udpdateInfo.anchor = GridBagConstraints.WEST; + gbc_udpdateInfo.gridx = 1; + gbc_udpdateInfo.gridy = 0; + panel_6.add(updateInfo, gbc_udpdateInfo); + + timeout = new JTextField(); GridBagConstraints gbc_updateTimeout = new GridBagConstraints(); gbc_updateTimeout.fill = GridBagConstraints.HORIZONTAL; gbc_updateTimeout.insets = new Insets(0, 0, 0, 5); - gbc_updateTimeout.gridx = 1; + gbc_updateTimeout.gridx = 2; gbc_updateTimeout.gridy = 0; - panel_6.add(updateTimeout, gbc_updateTimeout); - updateTimeout.setText("5000"); - updateTimeout.setColumns(10); + panel_6.add(timeout, gbc_updateTimeout); + timeout.setText("5000"); + timeout.setColumns(10); JLabel lblToms = new JLabel("Timeout (ms)"); GridBagConstraints gbc_lblToms = new GridBagConstraints(); - gbc_lblToms.anchor = GridBagConstraints.WEST; - gbc_lblToms.gridx = 2; + gbc_lblToms.anchor = GridBagConstraints.EAST; + gbc_lblToms.gridx = 3; gbc_lblToms.gridy = 0; panel_6.add(lblToms, gbc_lblToms); lblToms.setForeground(Color.BLACK); @@ -1309,20 +1300,14 @@ public void actionPerformed(ActionEvent e) { gbc_panel_7.gridy = 3; sparqlTab.add(panel_7, gbc_panel_7); GridBagLayout gbl_panel_7 = new GridBagLayout(); - gbl_panel_7.columnWidths = new int[] { 0, 0, 67, 73, 0, 0 }; + gbl_panel_7.columnWidths = new int[] { 0, 0, 67, 0 }; gbl_panel_7.rowHeights = new int[] { 0, 0 }; - gbl_panel_7.columnWeights = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; + gbl_panel_7.columnWeights = new double[] { 0.0, 1.0, 0.0, Double.MIN_VALUE }; gbl_panel_7.rowWeights = new double[] { 0.0, Double.MIN_VALUE }; panel_7.setLayout(gbl_panel_7); - queryButton = new JButton("QUERY"); - GridBagConstraints gbc_queryButton = new GridBagConstraints(); - gbc_queryButton.anchor = GridBagConstraints.WEST; - gbc_queryButton.insets = new Insets(0, 0, 0, 5); - gbc_queryButton.gridx = 0; - gbc_queryButton.gridy = 0; - panel_7.add(queryButton, gbc_queryButton); - queryButton.addActionListener(new ActionListener() { + btnQuery = new JButton("QUERY"); + btnQuery.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { query(); @@ -1331,14 +1316,26 @@ public void actionPerformed(ActionEvent e) { } } }); - queryButton.setForeground(UIManager.getColor("Desktop.background")); - queryButton.setEnabled(false); + GridBagConstraints gbc_btnQuery = new GridBagConstraints(); + gbc_btnQuery.anchor = GridBagConstraints.WEST; + gbc_btnQuery.insets = new Insets(0, 0, 0, 5); + gbc_btnQuery.gridx = 0; + gbc_btnQuery.gridy = 0; + panel_7.add(btnQuery, gbc_btnQuery); + + queryInfo = new JLabel("---"); + GridBagConstraints gbc_queryInfo = new GridBagConstraints(); + gbc_queryInfo.fill = GridBagConstraints.VERTICAL; + gbc_queryInfo.insets = new Insets(0, 0, 0, 5); + gbc_queryInfo.anchor = GridBagConstraints.WEST; + gbc_queryInfo.gridx = 1; + gbc_queryInfo.gridy = 0; + panel_7.add(queryInfo, gbc_queryInfo); subscribeButton = new JButton("SUBSCRIBE"); GridBagConstraints gbc_subscribeButton = new GridBagConstraints(); gbc_subscribeButton.anchor = GridBagConstraints.WEST; - gbc_subscribeButton.insets = new Insets(0, 0, 0, 5); - gbc_subscribeButton.gridx = 1; + gbc_subscribeButton.gridx = 2; gbc_subscribeButton.gridy = 0; panel_7.add(subscribeButton, gbc_subscribeButton); subscribeButton.addActionListener(new ActionListener() { @@ -1350,27 +1347,9 @@ public void actionPerformed(ActionEvent e) { } } }); - subscribeButton.setForeground(UIManager.getColor("Button.select")); + subscribeButton.setForeground(Color.BLACK); subscribeButton.setEnabled(false); - queryTimeout = new JTextField(); - GridBagConstraints gbc_queryTimeout = new GridBagConstraints(); - gbc_queryTimeout.fill = GridBagConstraints.HORIZONTAL; - gbc_queryTimeout.insets = new Insets(0, 0, 0, 5); - gbc_queryTimeout.gridx = 2; - gbc_queryTimeout.gridy = 0; - panel_7.add(queryTimeout, gbc_queryTimeout); - queryTimeout.setText("5000"); - queryTimeout.setColumns(10); - - JLabel lblTimeoutms = new JLabel("Timeout (ms)"); - GridBagConstraints gbc_lblTimeoutms = new GridBagConstraints(); - gbc_lblTimeoutms.insets = new Insets(0, 0, 0, 5); - gbc_lblTimeoutms.anchor = GridBagConstraints.WEST; - gbc_lblTimeoutms.gridx = 3; - gbc_lblTimeoutms.gridy = 0; - panel_7.add(lblTimeoutms, gbc_lblTimeoutms); - JScrollPane results = new JScrollPane(); GridBagConstraints gbc_results = new GridBagConstraints(); gbc_results.fill = GridBagConstraints.BOTH; @@ -1440,14 +1419,13 @@ public void actionPerformed(ActionEvent e) { GridBagLayout gbl_infoPanel = new GridBagLayout(); gbl_infoPanel.columnWidths = new int[] { 104, 88, 0, 0, 0, 97, 76, 0 }; gbl_infoPanel.rowHeights = new int[] { 29, 0 }; - gbl_infoPanel.columnWeights = new double[] { 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, - Double.MIN_VALUE }; + gbl_infoPanel.columnWeights = new double[] { 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, Double.MIN_VALUE }; gbl_infoPanel.rowWeights = new double[] { 0.0, Double.MIN_VALUE }; infoPanel.setLayout(gbl_infoPanel); JButton btnLoadXmlProfile = new JButton("Load JSAP"); - btnLoadXmlProfile.setForeground(UIManager.getColor("Button.light")); - btnLoadXmlProfile.setBackground(UIManager.getColor("Button.background")); + btnLoadXmlProfile.setForeground(Color.BLACK); + btnLoadXmlProfile.setBackground(Color.WHITE); GridBagConstraints gbc_btnLoadXmlProfile = new GridBagConstraints(); gbc_btnLoadXmlProfile.anchor = GridBagConstraints.WEST; gbc_btnLoadXmlProfile.insets = new Insets(0, 0, 0, 5); @@ -1478,7 +1456,7 @@ public void actionPerformed(ActionEvent e) { appProperties.put("jksName", jksName); appProperties.put("jksPass", jksPass); appProperties.put("keyPass", keyPass); - + try { appProperties.store(out, "Dashboard properties"); } catch (IOException e1) { @@ -1495,10 +1473,16 @@ public void actionPerformed(ActionEvent e) { } }); ToolTipManager.sharedInstance().setDismissDelay(Integer.MAX_VALUE); - - JButton btnRegister = new JButton("REGISTER"); + + btnRegister = new JButton("REGISTER"); + btnRegister.setEnabled(false); btnRegister.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + try { + sm.register(userID.getText()); + } catch (SEPASecurityException | SEPAPropertiesException e1) { + logger.error(e1.getMessage()); + } } }); GridBagConstraints gbc_btnRegister = new GridBagConstraints(); @@ -1506,8 +1490,9 @@ public void actionPerformed(ActionEvent e) { gbc_btnRegister.gridx = 1; gbc_btnRegister.gridy = 0; infoPanel.add(btnRegister, gbc_btnRegister); - + userID = new JTextField(); + userID.setEnabled(false); userID.setText("SEPATest"); GridBagConstraints gbc_userID = new GridBagConstraints(); gbc_userID.insets = new Insets(0, 0, 0, 5); @@ -1699,14 +1684,17 @@ else if (type.toUpperCase().equals("BNODE")) try { Instant start = Instant.now(); Response ret = sepaClient.query(queryID, querySPARQL.getText(), bindings, - Integer.parseInt(queryTimeout.getText())); + Integer.parseInt(timeout.getText())); Instant stop = Instant.now(); - if (ret.isError()) + if (ret.isError()) { logger.error(ret.toString() + String.format(" (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); - else { + queryInfo.setText("Error: " + ((ErrorResponse) ret).getErrorCode()); + } else { QueryResponse results = (QueryResponse) ret; logger.info(String.format("Results: %d (%d ms)", results.getBindingsResults().size(), (stop.toEpochMilli() - start.toEpochMilli()))); + queryInfo.setText(String.format("Results: %d (%d ms)", results.getBindingsResults().size(), + (stop.toEpochMilli() - start.toEpochMilli()))); bindingsDM.clear(); bindingsDM.setAddedResults(results.getBindingsResults(), null); } @@ -1732,12 +1720,14 @@ else if (type.equals("BNODE")) try { Instant start = Instant.now(); Response ret = sepaClient.update(updateID, updateSPARQL.getText(), bindings, - Integer.parseInt(updateTimeout.getText())); + Integer.parseInt(timeout.getText())); Instant stop = Instant.now(); - if (ret.isError()) + if (ret.isError()) { logger.error(ret.toString() + String.format(" (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); - else { + updateInfo.setText("Error: " + ((ErrorResponse) ret).getErrorCode()); + } else { logger.info(String.format("Update OK (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); + updateInfo.setText(String.format("Update OK (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); } } catch (NumberFormatException | SEPAProtocolException | SEPASecurityException | IOException e) { logger.error(e.getMessage()); @@ -1901,7 +1891,7 @@ private void enableUpdateButton() { } private void enableQueryButton() { - queryButton.setEnabled(false); + btnQuery.setEnabled(false); subscribeButton.setEnabled(false); if (querySPARQL.getText().equals("")) return; @@ -1913,7 +1903,7 @@ private void enableQueryButton() { return; } } - queryButton.setEnabled(true); + btnQuery.setEnabled(true); subscribeButton.setEnabled(true); } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java deleted file mode 100644 index 05ab4287..00000000 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/SEPATestClient.java +++ /dev/null @@ -1,381 +0,0 @@ -/* This program can be used and extended to test a SEPA implementation and API - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.tools; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; - -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.pattern.JSAP; -import it.unibo.arces.wot.sepa.pattern.GenericClient; -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -public class SEPATestClient { - protected Results results; - protected String spuid = null; - protected boolean notificationReceived = false; - protected Object sync = new Object(); - - protected long notificationMaxDelay = 2000; - protected JsonArray sequence; - - private JSAP appProfile; - private SubscriptionHandler handler = new SubscriptionHandler(); - private GenericClient client; - - class SubscriptionHandler implements ISubscriptionHandler { - @Override - public void onSemanticEvent(Notification notify) { - synchronized (sync) { - System.out.println(notify.toString()); - notificationReceived = true; - sync.notify(); - } - } - - @Override - public void onBrokenConnection() { - System.out.println("Broken socket!"); - } - - @Override - public void onError(ErrorResponse errorResponse) { - synchronized (sync) { - System.out.println(errorResponse.toString()); - sync.notify(); - } - } - } - - public SEPATestClient(JSAP appProfile) throws SEPAProtocolException, SEPASecurityException { - client = new GenericClient(appProfile); - - sequence = appProfile.getExtendedData().getAsJsonObject().get("sequence").getAsJsonArray(); - notificationMaxDelay = appProfile.getExtendedData().getAsJsonObject().get("notificationMaxDelay").getAsLong(); - - this.appProfile = appProfile; - } - - public SEPATestClient(JSAP appProfile,SEPASecurityManager sm) throws SEPAProtocolException, SEPASecurityException { - client = new GenericClient(appProfile,sm); - - sequence = appProfile.getExtendedData().getAsJsonObject().get("sequence").getAsJsonArray(); - notificationMaxDelay = appProfile.getExtendedData().getAsJsonObject().get("notificationMaxDelay").getAsLong(); - - this.appProfile = appProfile; - } - - class Results { - private long failed; - private ArrayList results = new ArrayList(); - - public void addResult(String title, boolean success) { - results.add(new Result(title, success)); - if (!success) - failed++; - } - - public void print() { - if (failed > 0) - System.out.println("*** TEST FAILED (" + failed + "/" + results.size() + ") ***"); - else - System.out.println("*** ვაიმეე TEST PASSED (" + results.size() + ") ვაიმეე ***"); - int index = 1; - for (Result res : results) { - res.print(index++); - } - } - } - - class Result { - private String title; - private boolean success; - - public Result(String title, boolean success) { - this.title = title; - this.success = success; - } - - public String toString() { - if (success) - title = title + " [PASSED]"; - else - title = title + " [FAILED]"; - return title; - } - - public void print(int index) { - if (success) - System.out.println(index + " " + toString()); - else - System.out.println(index + " " + toString()); - } - } - - public boolean updateTest(String id) throws SEPAPropertiesException { - - notificationReceived = false; - - String sparql = appProfile.getSPARQLUpdate(id); - - System.out.println("UPDATE: " + sparql); - - - Response response; - - try { - response =client.update(id, sparql, null,5000); - } catch (SEPAProtocolException | SEPASecurityException | IOException e) { - System.out.println(e.getMessage()); - return false; - } - - System.out.println(response.toString()); - - return response.isUpdateResponse(); - } - - public boolean queryTest(String id, int number) throws SEPAPropertiesException { - String sparql = appProfile.getSPARQLQuery(id); - - System.out.println("QUERY: " + sparql); - - Response response; - - try { - response =client.query(id, sparql, null, 5000); - } catch (SEPAProtocolException | SEPASecurityException | IOException e) { - System.out.println(e.getMessage()); - return false; - } - - System.out.println(response.toString()); - - if (response.isQueryResponse()) { - QueryResponse queryResponse = (QueryResponse) response; - List results = queryResponse.getBindingsResults().getBindings(); - return (results.size() == number); - } - - return false; - } - - public boolean subscribeTest(String id, long results) throws IOException, SEPAPropertiesException { - String sparql = appProfile.getSPARQLQuery(id); - - System.out.println("SUBSCRIBE: " + sparql); - - try { - Response response = client.subscribe(id,null,handler); - - System.out.println(response.toString()); - - if (response.isSubscribeResponse()) { - spuid = ((SubscribeResponse) response).getSpuid(); - return ((SubscribeResponse) response).getBindingsResults().size() == results; - } - } catch (SEPAProtocolException | SEPASecurityException e) { - System.err.println(e.getMessage()); - } - - return false; - } - - public boolean waitTokenToExpire() { - try { - System.out.println("Wait token to expire"); - Thread.sleep(appProfile.getAuthenticationProperties().getExpiringSeconds()); - } catch (InterruptedException e) { - return false; - } - - return true; - } - - public boolean waitNotification() { - synchronized (sync) { - if (notificationReceived) - return true; - try { - System.out.println("Wait notification"); - sync.wait(notificationMaxDelay); - } catch (InterruptedException e) { - System.out.println(e.getMessage()); - } - } - - return notificationReceived; - } - - public boolean unsubscribeTest(String spuid) throws SEPASecurityException, IOException, SEPAPropertiesException { - System.out.println("UNSUBSCRIBE: " + spuid); - - Response response = client.unsubscribe(spuid); - - System.out.println(response.toString()); - - return response.isUnsubscribeResponse(); - } - - public boolean registrationTest(String id) throws SEPASecurityException, SEPAPropertiesException { - Response response; - - System.out.println("REGISTER: " + id); - - response = client.register(id); - - System.out.println(response.toString()); - - return !response.getClass().equals(ErrorResponse.class); - } - - public boolean requestAccessTokenTest() throws SEPASecurityException, SEPAPropertiesException { - System.out.println("REQUEST ACCESS TOKEN"); - - Response response = new SEPASecurityManager().requestToken(appProfile.getAuthenticationProperties().getTokenRequestUrl(), appProfile.getAuthenticationProperties().getBasicAuthorizationHeader()); - - System.out.println(response.toString()); - - return !response.getClass().equals(ErrorResponse.class); - } - - public void run() throws SEPASecurityException, IOException, SEPAPropertiesException { - results = new Results(); - - boolean result = false; - String id = ""; - boolean passed = true; - String name = ""; - String type = ""; - - for (JsonElement test : sequence) { - type = test.getAsJsonObject().get("type").getAsString(); - switch (type) { - case "UPDATE": - case "QUERY": - case "SUBSCRIBE": - id = test.getAsJsonObject().get("id").getAsString(); - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - if (type.equals("UPDATE")) result = updateTest(id); - else if (type.equals("QUERY")) result = queryTest(id,test.getAsJsonObject().get("results").getAsInt()); - else result = subscribeTest(id,test.getAsJsonObject().get("results").getAsInt()); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "UNSUBSCRIBE": - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = unsubscribeTest(spuid); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "REGISTER": - id = test.getAsJsonObject().get("id").getAsString(); - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = registrationTest(id); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "GET_TOKEN": - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = requestAccessTokenTest(); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - case "WAIT_TOKEN_TO_EXPIRE": - result = waitTokenToExpire(); - results.addResult("Wait token to expire", result); - break; - case "WAIT_NOTIFICATION": - passed = test.getAsJsonObject().get("passed").getAsBoolean(); - name = test.getAsJsonObject().get("name").getAsString(); - - result = waitNotification(); - - if (passed) { - results.addResult(name, result); - } else { - results.addResult(name, !result); - } - break; - } - } - - results.print(); - } - - public static void main(String[] args) throws SEPASecurityException, SEPAProtocolException, SEPAPropertiesException, IOException { - System.out.println("**********************************************************"); - System.out.println("*** SPARQL 1.1 SE Protocol Service test suite ***"); - System.out.println("**********************************************************"); - - SEPASecurityManager sm = null; - JSAP jsap = null; - - if (args.length != 1) { - System.err.println("Usage: SEPATestClient "); - System.exit(-1); - } - - jsap = new JSAP(args[0]); - - SEPATestClient test = null; - if (jsap.isSecure()) { - sm = new SEPASecurityManager(); - test = new SEPATestClient(jsap,sm); - } - else test = new SEPATestClient(jsap); - - test.run(); - } -} diff --git a/tools/chat.jsap b/tools/src/main/resources/chat.jsap similarity index 100% rename from tools/chat.jsap rename to tools/src/main/resources/chat.jsap diff --git a/tools/src/main/resources/log4j2.xml b/tools/src/main/resources/log4j2.xml index 77af7ae9..5bf8b582 100644 --- a/tools/src/main/resources/log4j2.xml +++ b/tools/src/main/resources/log4j2.xml @@ -23,8 +23,8 @@ ALL Integer.MAX_VALUE
    - - + + diff --git a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java index b51b8e0c..a9eb077b 100644 --- a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java +++ b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java @@ -14,7 +14,7 @@ public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEP if( configuaration != null){ result = new JSAP(configuaration); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest-secure.jsap"); + URL config = Thread.currentThread().getContextClassLoader().getResource("chat.jsap"); result = new JSAP(config.getPath()); } return result; From 3885ea5fb931a5470e83fd616be92c6cb7195731 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 20 Jul 2018 17:51:40 +0200 Subject: [PATCH 34/76] Version 0.9.5 Tested with JUnit Improved subscription management Separation of internal messages structure (client and server) JMX fixing (need to be finalized) --- client-api/endpoint.jpar | 1 + .../wot/sepa/api/ISubscriptionHandler.java | 11 +- .../wot/sepa/api/ISubscriptionProtocol.java | 19 - .../wot/sepa/api/SPARQL11SEProtocol.java | 25 +- .../wot/sepa/api/SubscriptionProtocol.java | 21 + .../websocket/SPARQL11SESecureWebsocket.java | 46 -- .../websocket/SPARQL11SEWebsocket.java | 87 --- .../WebSocketSubscriptionProtocol.java | 67 --- .../SEPAWebsocketClient.java | 97 ++- .../WebsocketSubscriptionProtocol.java | 152 +++++ .../exceptions/SEPASecurityException.java | 4 + .../commons/protocol/SPARQL11Protocol.java | 71 +-- .../sepa/commons/request/QueryRequest.java | 71 +-- .../commons/request/RegistrationRequest.java | 1 - .../wot/sepa/commons/request/Request.java | 114 +--- .../sepa/commons/request/SPARQL11Request.java | 55 ++ .../commons/request/SubscribeRequest.java | 39 +- .../commons/request/UnsubscribeRequest.java | 26 +- .../sepa/commons/request/UpdateRequest.java | 81 +-- .../sepa/commons/response/ErrorResponse.java | 45 +- .../sepa/commons/response/JWTResponse.java | 2 +- .../sepa/commons/response/Notification.java | 1 - .../sepa/commons/response/QueryResponse.java | 20 - .../wot/sepa/commons/response/Response.java | 34 -- .../commons/response/SubscribeResponse.java | 93 +-- .../commons/response/UnsubscribeResponse.java | 30 +- .../sepa/commons/response/UpdateResponse.java | 33 -- .../security/AuthenticationProperties.java | 14 +- .../commons/security/SEPASecurityManager.java | 51 +- .../arces/wot/sepa/pattern/Aggregator.java | 17 +- .../unibo/arces/wot/sepa/pattern/Client.java | 52 +- .../arces/wot/sepa/pattern/Consumer.java | 64 +- .../arces/wot/sepa/pattern/GenericClient.java | 146 ++--- .../arces/wot/sepa/pattern/IConsumer.java | 6 +- .../arces/wot/sepa/pattern/IProducer.java | 1 - .../arces/wot/sepa/pattern/Producer.java | 18 +- .../unibo/arces/wot/sepa/timing/Timings.java | 26 +- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 558 ++++++++++++++---- .../wot/sepa/api/MockSubscriptionHandler.java | 35 -- .../unibo/arces/wot/sepa/api/Publisher.java | 7 +- .../unibo/arces/wot/sepa/api/Subscriber.java | 117 ++-- .../websocket/ConfigurationProvider.java | 22 + .../websocket/ITSEPAWebsocketClient.java | 118 ---- .../ITWebSocketSubscriptionProtocol.java | 315 ++++++++++ .../protocol/websocket/WebsocketClient.java | 39 ++ .../security/ConfigurationProvider.java | 22 + .../security/ITSEPASecurityManager.java | 48 ++ client-api/src/test/resources/log4j2.xml | 33 ++ .../src/test/resources/sepatest-secure.jsap | 22 +- client-api/src/test/resources/sepatest.jsap | 6 +- .../sepa/engine/bean/HTTPHandlerBeans.java | 12 + .../wot/sepa/engine/bean/SchedulerBeans.java | 16 +- .../wot/sepa/engine/bean/WebsocketBeans.java | 60 +- .../arces/wot/sepa/engine/core/Engine.java | 21 +- .../wot/sepa/engine/core/EventHandler.java | 7 +- .../wot/sepa/engine/core/ResponseHandler.java | 5 +- .../dependability/AuthorizationManager.java | 22 +- .../dependability/DependabilityManager.java | 81 ++- .../wot/sepa/engine/processing/Processor.java | 165 ++++++ ...orThreadMBean.java => ProcessorMBean.java} | 2 +- .../engine/processing/ProcessorThread.java | 214 ------- .../processing/QueryProcessingThread.java | 30 + .../engine/processing/QueryProcessor.java | 15 +- .../engine/processing/SpuKillerThread.java | 35 -- .../processing/SubscribeProcessingThread.java | 293 +++++++++ ...va => SubscribeProcessingThreadMBean.java} | 2 +- .../engine/processing/SubscribeProcessor.java | 328 ---------- .../processing/UpdateProcessingThread.java | 63 +- .../engine/processing/UpdateProcessor.java | 17 +- .../engine/processing/subscriptions/ISPU.java | 4 +- .../engine/processing/subscriptions/SPU.java | 122 ++-- .../processing/subscriptions/SPUManager.java | 209 +++---- .../processing/subscriptions/SPUNaive.java | 26 +- .../processing/subscriptions/Subscriber.java | 60 -- .../subscriptions/Unsubscriber.java | 57 -- .../protocol/http/handler/QueryHandler.java | 9 +- .../http/handler/SPARQL11Handler.java | 35 +- .../http/handler/SPARQL11ResponseHandler.java | 2 +- .../protocol/http/handler/UpdateHandler.java | 4 +- .../websocket/SecureWebsocketServer.java | 21 +- .../websocket/WebsocketEventHandler.java | 48 +- .../protocol/websocket/WebsocketServer.java | 75 +-- .../scheduling/InternalQueryRequest.java | 19 + .../engine/scheduling/InternalRequest.java | 20 + .../scheduling/InternalSubscribeRequest.java | 39 ++ .../engine/scheduling/InternalUQRequest.java | 30 + .../InternalUnsubscribeRequest.java | 24 + .../scheduling/InternalUpdateRequest.java | 19 + .../engine/scheduling/ScheduledRequest.java | 21 +- .../engine/scheduling/ScheduledResponse.java | 26 + .../wot/sepa/engine/scheduling/Scheduler.java | 146 ++--- .../engine/scheduling/SchedulerQueue.java | 119 ++++ .../SchedulerRequestResponseQueue.java | 26 - .../arces/wot/sepa/engine/timing/Timings.java | 49 ++ engine/src/main/resources/log4j2.xml | 4 +- .../test/java/engine/QueryProcessorTest.java | 30 - .../src/test/java/engine/SchedulerTest.java | 51 -- .../test/java/engine/UpdateProcessorTest.java | 30 - .../subscription/SPUMangerTest.java | 89 --- engine/src/test/resources/endpoint.jpar | 33 ++ engine/src/test/resources/engine-secure.jpar | 33 ++ engine/src/test/resources/engine.jpar | 33 ++ .../arces/wot/sepa/apps/chat/BasicClient.java | 21 +- .../wot/sepa/apps/chat/BasicHandler.java | 17 +- .../arces/wot/sepa/apps/chat/ChatClient.java | 16 +- .../arces/wot/sepa/apps/chat/Receiver.java | 55 +- .../arces/wot/sepa/apps/chat/Remover.java | 53 +- .../wot/sepa/apps/chat/UpdateQueryTest.java | 6 +- .../unibo/arces/wot/sepa/apps/chat/Users.java | 54 +- .../arces/wot/sepa/apps/mqtt/MQTTMonitor.java | 29 +- .../wot/sepa/apps/mqtt/MQTTSmartifier.java | 51 +- .../wot/sepa/apps/mqtt/ObservationLogger.java | 12 + .../sepa/apps/mqtt/ObservationSimulator.java | 2 +- .../unibo/arces/wot/sepa/tools/Dashboard.java | 178 +++--- .../wot/sepa/apps/chat/SEPAChatTest.java | 4 +- 115 files changed, 3226 insertions(+), 3156 deletions(-) create mode 100644 client-api/endpoint.jpar delete mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java delete mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java delete mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java delete mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java rename client-api/src/main/java/it/unibo/arces/wot/sepa/api/{protocol/websocket => protocols}/SEPAWebsocketClient.java (52%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SPARQL11Request.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java create mode 100644 client-api/src/test/resources/log4j2.xml create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/{ProcessorThreadMBean.java => ProcessorMBean.java} (89%) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/{SubscribeProcessorMBean.java => SubscribeProcessingThreadMBean.java} (92%) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledResponse.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java delete mode 100644 engine/src/test/java/engine/QueryProcessorTest.java delete mode 100644 engine/src/test/java/engine/SchedulerTest.java delete mode 100644 engine/src/test/java/engine/UpdateProcessorTest.java delete mode 100644 engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java create mode 100644 engine/src/test/resources/endpoint.jpar create mode 100644 engine/src/test/resources/engine-secure.jpar create mode 100644 engine/src/test/resources/engine.jpar diff --git a/client-api/endpoint.jpar b/client-api/endpoint.jpar new file mode 100644 index 00000000..5ea97c64 --- /dev/null +++ b/client-api/endpoint.jpar @@ -0,0 +1 @@ +{"host":"localhost","sparql11protocol":{"protocol":"http","port":9999,"query":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"},"update":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"ws","availableProtocols":{"ws":{"port":9443,"path":"/subscribe"},"wss":{}}}} \ No newline at end of file diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java index 52f19681..c051b31c 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java @@ -18,7 +18,6 @@ package it.unibo.arces.wot.sepa.api; -import it.unibo.arces.wot.sepa.api.protocol.websocket.SEPAWebsocketClient; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; @@ -49,4 +48,14 @@ public interface ISubscriptionHandler { * @see ErrorResponse */ void onError(ErrorResponse errorResponse); + + /** + * This method is called when the first notification has been received + */ + void onSubscribe(String spuid,String alias); + + /** + * This method is called as response to an unsubscribe request + */ + void onUnsubscribe(String spuid); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java deleted file mode 100644 index 42c52210..00000000 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionProtocol.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.Response; - -public interface ISubscriptionProtocol { - - boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException; - - void close(); - - Response subscribe(SubscribeRequest request); - - Response unsubscribe(UnsubscribeRequest request); - - boolean isSecure(); -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java index 02f7b231..a71a8322 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java @@ -45,22 +45,19 @@ public class SPARQL11SEProtocol extends SPARQL11Protocol { private static final Logger logger = LogManager.getLogger(); - private ISubscriptionProtocol subscriptionProtocol; + private SubscriptionProtocol subscriptionProtocol; - public SPARQL11SEProtocol(ISubscriptionProtocol protocol,ISubscriptionHandler handler, SEPASecurityManager sm) throws SEPAProtocolException { + public SPARQL11SEProtocol(SubscriptionProtocol protocol, SEPASecurityManager sm) throws SEPAProtocolException { super(sm); if (protocol == null) throw new IllegalArgumentException("Protocol is null"); - if (handler == null) throw new IllegalArgumentException("Handler is null"); - if (!protocol.isSecure()) throw new SEPAProtocolException(new IllegalArgumentException("Mixing secure and not secure protocols is not allowed")); this.subscriptionProtocol = protocol; - this.subscriptionProtocol.connect(handler); } - public SPARQL11SEProtocol(ISubscriptionProtocol protocol,ISubscriptionHandler handler) throws IllegalArgumentException, SEPAProtocolException { - if (protocol == null || handler == null) { + public SPARQL11SEProtocol(SubscriptionProtocol protocol) throws SEPAProtocolException { + if (protocol == null ) { logger.error("One or more arguments are null"); throw new IllegalArgumentException("One or more arguments are null"); } @@ -68,7 +65,6 @@ public SPARQL11SEProtocol(ISubscriptionProtocol protocol,ISubscriptionHandler ha if (protocol.isSecure()) throw new SEPAProtocolException(new IllegalArgumentException("Security parameters are missing")); this.subscriptionProtocol = protocol; - this.subscriptionProtocol.connect(handler); } public boolean isSecure() { @@ -82,11 +78,12 @@ public boolean isSecure() { * @param request * @return A valid {@link Response} if the subscription is successful
    * an {@link ErrorResponse} otherwise + * @throws SEPAProtocolException */ - public Response subscribe(SubscribeRequest request) { + public void subscribe(SubscribeRequest request) throws SEPAProtocolException { logger.debug(request.toString()); - return subscriptionProtocol.subscribe(request); + subscriptionProtocol.subscribe(request); } /** @@ -98,20 +95,22 @@ public Response subscribe(SubscribeRequest request) { * @param request * @return A valid {@link Response} if the unsubscription is successful
    * an {@link ErrorResponse} otherwise + * @throws SEPAProtocolException */ - public Response unsubscribe(UnsubscribeRequest request) { + public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { logger.debug(request.toString()); - return subscriptionProtocol.unsubscribe(request); + subscriptionProtocol.unsubscribe(request); } /** * Free the http connection manager and the WebSocket client. + * @throws SEPAProtocolException * * @throws IOException */ @Override - public void close() throws IOException { + public void close() { super.close(); subscriptionProtocol.close(); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java new file mode 100644 index 00000000..7bfe7607 --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java @@ -0,0 +1,21 @@ +package it.unibo.arces.wot.sepa.api; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; + +public abstract class SubscriptionProtocol { + protected ISubscriptionHandler handler; + + public SubscriptionProtocol(ISubscriptionHandler handler) { + this.handler = handler; + } + + public abstract void close(); + + public abstract void subscribe(SubscribeRequest request) throws SEPAProtocolException; + + public abstract void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException; + + public abstract boolean isSecure(); +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java deleted file mode 100644 index c0b79ff4..00000000 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SESecureWebsocket.java +++ /dev/null @@ -1,46 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import java.io.IOException; -import java.net.Socket; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -public class SPARQL11SESecureWebsocket extends SPARQL11SEWebsocket { - private Socket secureSocket = null; - - public SPARQL11SESecureWebsocket(String wsUrl,SEPASecurityManager sm) - throws SEPAProtocolException, SEPASecurityException { - super(wsUrl); - - try { - secureSocket = sm.getSSLSocket(); - } catch (IOException e) { - throw new SEPASecurityException(e); - } - } - - public boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException { - if (handler == null) { - logger.fatal("Notification handler is null. Client cannot be initialized"); - throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); - } - - if (client == null) { - client = new SEPAWebsocketClient(wsURI, handler); - - // Enable secure socket - client.setSocket(secureSocket); - - try { - return client.connectBlocking(); - } catch (InterruptedException e) { - throw new SEPAProtocolException(e); - } - } - - return client.isOpen(); - } -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java deleted file mode 100644 index 803dd2a5..00000000 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SPARQL11SEWebsocket.java +++ /dev/null @@ -1,87 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; - -public class SPARQL11SEWebsocket { - protected Logger logger = LogManager.getLogger(); - - private long TIMEOUT = 5000; - - protected SEPAWebsocketClient client = null; - protected URI wsURI = null; - - public SPARQL11SEWebsocket(String wsUrl) throws SEPAProtocolException { - try { - wsURI = new URI(wsUrl); - } catch (URISyntaxException e) { - logger.fatal(e.getMessage()); - throw new SEPAProtocolException(e); - } - } - - public boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException { - if (handler == null) { - logger.fatal("Notification handler is null. Client cannot be initialized"); - throw new SEPAProtocolException(new IllegalArgumentException("Notificaton handler is null")); - } - - if (client == null) { - client = new SEPAWebsocketClient(wsURI, handler); - - try { - while(!client.connectBlocking()) { - Thread.sleep(100); - } - } catch (InterruptedException e) { - throw new SEPAProtocolException(e); - } - } - - return client.isOpen(); - } - - public Response subscribe(SubscribeRequest req) { - if (req.getSPARQL() == null) { - logger.error("SPARQL query is null"); - return new ErrorResponse(-1,500, "SPARQL query is null"); - } - - if (client == null) { - logger.error("Client is null"); - return new ErrorResponse(-1,500, "Client is null"); - } - - // Send request and wait for response - return client.sendAndReceive(req.toString(), req.getTimeout()); - } - - public Response unsubscribe(UnsubscribeRequest req) { - if (req.getSubscribeUUID() == null) - return new ErrorResponse(-1,500, "SPUID is null"); - - if (client == null) - return new ErrorResponse(-1,500, "Client is null"); - - if (client.isClosed()) - return new ErrorResponse(-1,500, "Socket is closed"); - - // Send request and wait for response - return client.sendAndReceive(req.toString(), TIMEOUT); - } - - public void close() { - if (client != null) - client.close(); - } -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java deleted file mode 100644 index 7dda1194..00000000 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebSocketSubscriptionProtocol.java +++ /dev/null @@ -1,67 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -public class WebSocketSubscriptionProtocol implements ISubscriptionProtocol { - private SPARQL11SEWebsocket wsClient = null; - private SPARQL11SESecureWebsocket wssClient = null; - - public WebSocketSubscriptionProtocol(String host, int port, String path) throws SEPAProtocolException { - // WS - if (port != -1) - wsClient = new SPARQL11SEWebsocket("ws://" + host + ":" + port + path); - else - wsClient = new SPARQL11SEWebsocket("ws://" + host + path); - } - - public WebSocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm) - throws SEPAProtocolException, SEPASecurityException { - // WSS - if (port != -1) - wssClient = new SPARQL11SESecureWebsocket("wss://" + host + ":" + port + path, sm); - else - wssClient = new SPARQL11SESecureWebsocket("wss://" + host + path, sm); - - } - - @Override - public boolean connect(ISubscriptionHandler handler) throws SEPAProtocolException { - if (!isSecure()) return wsClient.connect(handler); - else return wssClient.connect(handler); - } - - @Override - public void close() { - if (!isSecure()) - wsClient.close(); - else - wssClient.close(); - } - - @Override - public Response subscribe(SubscribeRequest request) { - if (!isSecure()) return wsClient.subscribe(request); - - return wssClient.subscribe(request); - } - - @Override - public Response unsubscribe(UnsubscribeRequest request) { - if (!isSecure()) - return wsClient.unsubscribe(request); - else - return wssClient.unsubscribe(request); - } - - @Override - public boolean isSecure() { - return wssClient != null; - } -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/SEPAWebsocketClient.java similarity index 52% rename from client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/SEPAWebsocketClient.java index d4a87720..fcd196b5 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocol/websocket/SEPAWebsocketClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/SEPAWebsocketClient.java @@ -1,12 +1,11 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; +package it.unibo.arces.wot.sepa.api.protocols; +import java.net.Socket; import java.net.URI; -import java.util.concurrent.Semaphore; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.java_websocket.client.WebSocketClient; -import org.java_websocket.exceptions.WebsocketNotConnectedException; import org.java_websocket.handshake.ServerHandshake; import com.google.gson.JsonObject; @@ -15,19 +14,13 @@ import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; -public class SEPAWebsocketClient extends WebSocketClient { +class SEPAWebsocketClient extends WebSocketClient { protected final Logger logger = LogManager.getLogger(); private ISubscriptionHandler handler; - private Response response = new ErrorResponse(500, "null"); - private Semaphore mutex = new Semaphore(0); - private boolean responseReceived = false; - public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { + public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler, Socket secure) { super(wsUrl); if (handler == null) { @@ -36,45 +29,19 @@ public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { } this.handler = handler; - } - - private Response waitResponse(long timeout) { - synchronized (mutex) { - while (!responseReceived) { - try { - mutex.wait(timeout); - } catch (InterruptedException e) { - return new ErrorResponse(-1,500, "Interrupted exception"); - } - } - } - return response; + setSocket(secure); } - private void setResponse(Response response) { - synchronized (mutex) { - this.response = response; - responseReceived = true; - mutex.notify(); - } - } - - public Response sendAndReceive(String message, long timeout) { - // Send request and wait response - responseReceived = false; + public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { + super(wsUrl); - try { - if (isOpen()) { - send(message); - return waitResponse(timeout); - } - } catch (WebsocketNotConnectedException e) { - return new ErrorResponse(-1,500, "WebsocketNotConnectedException"); + if (handler == null) { + logger.fatal("Notification handler is null. Client cannot be initialized"); + throw new IllegalArgumentException("Notificaton handler is null"); } - return new ErrorResponse(-1,500, "Socket is closed"); - + this.handler = handler; } @Override @@ -85,19 +52,22 @@ public void onOpen(ServerHandshake handshakedata) { @Override public void onClose(int code, String reason, boolean remote) { logger.debug( - "@onClose code: " + code + " reason: " + reason + " remote: " + remote + " STATE: " + getReadyState()); + "@onClose code:" + code + " reason:" + reason + " remote:" + remote + " state:" + getReadyState()); if (handler != null) handler.onBrokenConnection(); } @Override public void onError(Exception ex) { - ErrorResponse error = new ErrorResponse(-1,500, "onError: "+ex.getMessage()); + ErrorResponse error = new ErrorResponse(500, "onError: " + ex.getMessage()); logger.debug("@onError: " + error + " STATE: " + getReadyState()); - if (handler != null) + try { handler.onError(error); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } } @Override @@ -116,15 +86,36 @@ public void onMessage(String message) { if (jsonMessage.has("notification")) { JsonObject notification = jsonMessage.get("notification").getAsJsonObject(); if (notification.get("sequence").getAsInt() == 0) { - setResponse(new SubscribeResponse(jsonMessage)); - } else if (handler != null) + String spuid = notification.get("spuid").getAsString(); + String alias = null; + if (notification.has("alias")) + alias = notification.get("alias").getAsString(); + try { + handler.onSubscribe(spuid, alias); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + return; + } + } + try { handler.onSemanticEvent(new Notification(jsonMessage)); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } } else if (jsonMessage.has("error")) { - setResponse(new ErrorResponse(jsonMessage)); - if (handler != null) - handler.onError((ErrorResponse) response); + try{ + handler.onError(new ErrorResponse(jsonMessage)); + } + catch(Exception e) { + logger.error("Handler is null "+e.getMessage()); + } } else if (jsonMessage.has("unsubscribed")) { - setResponse(new UnsubscribeResponse(jsonMessage)); + try { + handler.onUnsubscribe(jsonMessage.get("unsubscribed").getAsJsonObject().get("spuid").getAsString()); + } + catch(Exception e) { + logger.error("Handler is null "+e.getMessage()); + } } else logger.error("Unknown message: " + message); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java new file mode 100644 index 00000000..14a9e689 --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java @@ -0,0 +1,152 @@ +package it.unibo.arces.wot.sepa.api.protocols; + +import java.io.IOException; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.NotYetConnectedException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +//import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +//import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +public class WebsocketSubscriptionProtocol extends SubscriptionProtocol { + protected final Logger logger = LogManager.getLogger(); + + private SEPAWebsocketClient client = null; + + private Socket secure = null; + private URI url; + + public WebsocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm, + ISubscriptionHandler handler) throws SEPAProtocolException { + super(handler); + if (sm == null) { + logger.error("Security manager is null"); + throw new IllegalArgumentException("Security manager is null"); + } + + try { + url = new URI("wss://" + host + ":" + port + path); + } catch (URISyntaxException e1) { + throw new SEPAProtocolException(e1); + } + + try { + secure = sm.getSSLSocket(); + } catch (IOException e) { + throw new SEPAProtocolException(e); + } + } + + public WebsocketSubscriptionProtocol(String host, String path, SEPASecurityManager sm, ISubscriptionHandler handler) + throws SEPAProtocolException { + super(handler); + if (sm == null) { + logger.error("Security manager is null"); + throw new IllegalArgumentException("Security manager is null"); + } + + try { + url = new URI("wss://" + host + path); + } catch (URISyntaxException e1) { + throw new SEPAProtocolException(e1); + } + + try { + secure = sm.getSSLSocket(); + } catch (IOException e) { + throw new SEPAProtocolException(e); + } + } + + public WebsocketSubscriptionProtocol(String host, int port, String path, ISubscriptionHandler handler) + throws SEPAProtocolException { + super(handler); + try { + url = new URI("ws://" + host + ":" + port + path); + } catch (URISyntaxException e) { + throw new SEPAProtocolException(e); + } + } + + public WebsocketSubscriptionProtocol(String host, String path, ISubscriptionHandler handler) + throws SEPAProtocolException { + super(handler); + try { + url = new URI("ws://" + host + path); + } catch (URISyntaxException e) { + throw new SEPAProtocolException(e); + } + } + + private void connect() throws SEPAProtocolException { + if (client == null) { + if (secure != null) + client = new SEPAWebsocketClient(url, handler, secure); + else + client = new SEPAWebsocketClient(url, handler); + + try { + if (!client.connectBlocking()) { + logger.error("Failed to connect"); + throw new SEPAProtocolException("Failed to connect"); + } + } catch (InterruptedException e) { + throw new SEPAProtocolException(e); + } + + logger.debug("Connected"); + } + } + + @Override + public void subscribe(SubscribeRequest request) throws SEPAProtocolException { + connect(); + + try { + client.send(request.toString()); + } catch (NotYetConnectedException e) { + throw new SEPAProtocolException(e); + } + // try { + // connect(); + // } catch (SEPAProtocolException e) { + // return new ErrorResponse(500,"Failed to connect"); + // } + // logger.debug("Subscribe: " + request.toString()); + // return client.sendAndReceive(request.toString(), request.getTimeout()); + } + + @Override + public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { + try { + client.send(request.toString()); + } catch (NotYetConnectedException e) { + throw new SEPAProtocolException(e); + } + + // logger.debug("Unsubscribe: " + request.toString()); + // return client.sendAndReceive(request.toString(), request.getTimeout()); + } + + @Override + public boolean isSecure() { + return secure != null; + } + + @Override + public void close() { + if (client != null) + client.close(); + } + +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java index b2d9ff40..cd187cd3 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPASecurityException.java @@ -9,4 +9,8 @@ public class SEPASecurityException extends Exception { public SEPASecurityException(Throwable e){ super.initCause(e); } + + public SEPASecurityException(String string) { + super.initCause(new Exception(string)); + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index 352ac6a8..da7c8164 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -122,48 +122,45 @@ private Response executeRequest(HttpUriRequest req, Request request) { // Body responseEntity = httpResponse.getEntity(); responseBody = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); - if (request.getToken() != -1) - logger.debug(String.format("Response code: %d #%d", responseCode, request.getToken())); - else - logger.debug(String.format("Response code: %d", responseCode)); + logger.trace(String.format("Response code: %d", responseCode)); EntityUtils.consume(responseEntity); // http://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/fundamentals.html#d5e279 } catch (IOException e) { if (e instanceof InterruptedIOException) { - return new ErrorResponse(request.getToken(), HttpStatus.SC_SERVICE_UNAVAILABLE, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, e.getMessage()); } if (e instanceof UnknownHostException) { - return new ErrorResponse(request.getToken(), HttpStatus.SC_NOT_FOUND, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_NOT_FOUND, e.getMessage()); } if (e instanceof ConnectTimeoutException) { - return new ErrorResponse(request.getToken(), HttpStatus.SC_REQUEST_TIMEOUT, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_REQUEST_TIMEOUT, e.getMessage()); } if (e instanceof SSLException) { - return new ErrorResponse(request.getToken(), HttpStatus.SC_UNAUTHORIZED, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, e.getMessage()); } - return new ErrorResponse(request.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } finally { try { if (httpResponse != null) httpResponse.close(); } catch (IOException e) { - return new ErrorResponse(request.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } responseEntity = null; } if (responseCode >= 400) - return new ErrorResponse(request.getToken(), responseCode, responseBody); + return new ErrorResponse(responseCode, responseBody); if (request.getClass().equals(UpdateRequest.class)) - return new UpdateResponse(request.getToken(), responseBody); + return new UpdateResponse(responseBody); try { - return new QueryResponse(request.getToken(), new JsonParser().parse(responseBody).getAsJsonObject()); + return new QueryResponse(new JsonParser().parse(responseBody).getAsJsonObject()); } catch (Exception e) { - return new ErrorResponse(request.getToken(), HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, e.getMessage()); } } @@ -224,7 +221,7 @@ public Response update(UpdateRequest req) { case URL_ENCODED_POST: return post(req); default: - return new ErrorResponse(req.getToken(), HttpStatus.SC_BAD_REQUEST, + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "SPARQL 1.1 Update supports POST method only"); } } @@ -260,13 +257,13 @@ private Response post(UpdateRequest req) { // Create POST request try { if (req.getHttpMethod().equals(HTTPMethod.POST)) { - if (req.getUsingGraphUri() != null) { - graphs = "using-graph-uri=" + req.getUsingGraphUri(); - if (req.getUsingNamedGraphUri() != null) { - graphs += "&using-named-graph-uri=" + req.getUsingNamedGraphUri(); + if (req.getDefaultGraphUri() != null) { + graphs = "using-graph-uri=" + req.getDefaultGraphUri(); + if (req.getNamedGraphUri() != null) { + graphs += "&using-named-graph-uri=" + req.getNamedGraphUri(); } - } else if (req.getUsingNamedGraphUri() != null) { - graphs = "using-named-graph-uri=" + req.getUsingNamedGraphUri(); + } else if (req.getNamedGraphUri() != null) { + graphs = "using-named-graph-uri=" + req.getNamedGraphUri(); } post = new HttpPost(new URI(scheme, null, host, port, updatePath, graphs, null)); @@ -280,17 +277,17 @@ private Response post(UpdateRequest req) { // Graphs try { - if (req.getUsingGraphUri() != null) { - graphs = "using-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), "UTF-8"); - if (req.getUsingNamedGraphUri() != null) { - graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + if (req.getDefaultGraphUri() != null) { + graphs = "using-graph-uri=" + URLEncoder.encode(req.getDefaultGraphUri(), "UTF-8"); + if (req.getNamedGraphUri() != null) { + graphs += "&using-named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); } - } else if (req.getUsingNamedGraphUri() != null) { - graphs = "using-named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); + } else if (req.getNamedGraphUri() != null) { + graphs = "using-named-graph-uri=" + URLEncoder.encode(req.getNamedGraphUri(), "UTF-8"); } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } // Body @@ -301,7 +298,7 @@ private Response post(UpdateRequest req) { } } catch (URISyntaxException | UnsupportedEncodingException e) { logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } // Accept header @@ -571,7 +568,7 @@ private Response post(QueryRequest req) { } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } // Body @@ -582,7 +579,7 @@ private Response post(QueryRequest req) { } } catch (URISyntaxException | UnsupportedEncodingException e) { logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } post.setHeader("Accept", req.getAcceptHeader()); @@ -611,7 +608,7 @@ private Response get(QueryRequest req) { query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"); } catch (UnsupportedEncodingException e1) { logger.error(e1.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage()); } String graphs = ""; @@ -628,7 +625,7 @@ private Response get(QueryRequest req) { } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); - return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse( HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } if (!graphs.equals("")) @@ -659,7 +656,11 @@ private Response get(QueryRequest req) { } @Override - public void close() throws IOException { - httpClient.close(); + public void close() { + try { + httpClient.close(); + } catch (IOException e) { + logger.error(e.getMessage()); + } } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java index 1fd5e555..e63cfd54 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/QueryRequest.java @@ -26,75 +26,27 @@ * @see SPARQL 1.1 Query * */ -public class QueryRequest extends Request { - protected String default_graph_uri = null; - protected String named_graph_uri = null; - - /** - * Instantiates a new query request. - * - * @param token the token of the request - * @param sparql the SPARQL 1.1 Query - */ - public QueryRequest(Integer token, String sparql) { - super(token, sparql); - } - +public class QueryRequest extends SPARQL11Request { /** * Instantiates a new query request. * * @param sparql the SPARQL 1.1 Query */ - public QueryRequest(String sparql) { - super(sparql); - } - - public QueryRequest(String sparql,String default_graph_uri,String named_graph_uri) { - super(sparql); - - this.default_graph_uri = default_graph_uri; - this.named_graph_uri = named_graph_uri; - } - public QueryRequest(Integer token,HTTPMethod method,String scheme,String host, int port, String path,String sparql,int timeout,String default_graph_uri,String named_graph_uri,String authorization) { - super(token,sparql); - - super.method = method; - super.host = host; - super.port = port; - super.path = path; - super.timeout = timeout; - super.scheme = scheme; - - super.authorizationHeader = authorization; - - this.default_graph_uri = default_graph_uri; - this.named_graph_uri = named_graph_uri; - } - - public QueryRequest(HTTPMethod queryMethod, String queryProtocolScheme, String queryHost, int queryPort, - String queryPath, String string, int timeout, String defaultGraphURI, String namedGraphURI,String authorization) { - this(-1,queryMethod, queryProtocolScheme, queryHost, queryPort,queryPath, string, timeout, defaultGraphURI, namedGraphURI,authorization); - } - - @Override - public String toString() { - if (token != -1) return "QUERY #"+token+" "+sparql; - return "QUERY "+sparql; + public QueryRequest(HTTPMethod method,String scheme,String host, int port, String path,String sparql,String default_graph_uri,String named_graph_uri,String authorization,long timeout) { + super(sparql,authorization,default_graph_uri,named_graph_uri,timeout); + this.method = method; + this.host = host; + this.port = port; + this.path = path; + this.timeout = timeout; + this.scheme = scheme; } public String getAcceptHeader() { return "application/sparql-results+json"; } - - public String getDefaultGraphUri() { - return default_graph_uri; - } - - public String getNamedGraphUri() { - return named_graph_uri; - } /** * Default implementation. Two requests are equal if they belong to the same class and their SPARQL strings are equals. SPARQL matching should be based on SPARQL algebra @@ -105,9 +57,4 @@ public boolean equals(Object obj) { if (!(obj instanceof QueryRequest)) return false; return sparql.equals(((QueryRequest)obj).sparql); } - - @Override - public int hashCode() { - return sparql.hashCode(); - } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java index 428ad930..2a8b0a63 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/RegistrationRequest.java @@ -22,7 +22,6 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -// TODO: Auto-generated Javadoc /** * The Class RegistrationRequest. */ diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java index a0f13fe3..8152d62e 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/Request.java @@ -17,52 +17,21 @@ */ package it.unibo.arces.wot.sepa.commons.request; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; - /** * This class represents a generic request (i.e., QUERY, UPDATE, SUBSCRIBE, * UNSUBSCRIBE) */ public abstract class Request { - - /** The token. */ - protected int token = -1; - - /** The sparql. */ - protected String sparql; - -// /** -// * Authorization related members -// * -// * 1) The 'Basic' HTTP Authentication Scheme, -// * https://tools.ietf.org/html/rfc7617 -// */ -// -// protected enum AUTHENTICATION_SCHEMA { -// DISABLED, BASIC -// }; -// -// protected AUTHENTICATION_SCHEMA authorization = AUTHENTICATION_SCHEMA.DISABLED; -// protected String basicAuthorizationHeader; - - protected HTTPMethod method = HTTPMethod.POST; - protected String id = null; - protected long timeout = 5000; - - protected String scheme = null; - protected String host = null; - protected int port = -1; - protected String path = null; - protected String authorizationHeader = null; + /** The sparql. */ + protected String sparql; + /** * Instantiates a new request. * - * @param token - * the token * @param sparql * the sparql * @param auth @@ -71,47 +40,16 @@ public abstract class Request { * The 'Basic' HTTP Authentication Scheme, https://tools.ietf.org/html/rfc7617 */ - public Request(int token, String sparql,String auth) { - this.token = token; + public Request(String sparql,String auth,long timeout) { this.sparql = sparql; this.authorizationHeader = auth; } - - public Request(int token, String sparql) { - this.token = token; - this.sparql = sparql; - } - /** - * Instantiates a new request. - * - * @param sparql - * the sparql - */ - public Request(String sparql) { - this.token = -1; - this.sparql = sparql; + @Override + public int hashCode() { + return sparql.hashCode(); } - public Request(String sparql,String auth) { - this.token = -1; - this.sparql = sparql; - this.authorizationHeader = auth; - } - - public void setToken(int token) { - this.token = token; - } - - /** - * Gets the token. - * - * @return the token - */ - public int getToken() { - return token; - } - /** * Gets the sparql. * @@ -121,6 +59,10 @@ public String getSPARQL() { return sparql; } + public String getAuthorizationHeader() { + return authorizationHeader; + } + public boolean isQueryRequest() { return this.getClass().equals(QueryRequest.class); } @@ -136,15 +78,7 @@ public boolean isSubscribeRequest() { public boolean isUnsubscribeRequest() { return this.getClass().equals(UnsubscribeRequest.class); } - - public HTTPMethod getHttpMethod() { - return method; - } - - public String getID() { - return id; - } - + public long getTimeout() { return timeout; } @@ -152,28 +86,4 @@ public long getTimeout() { public void setTimeout(int timeout) { this.timeout = timeout; } - - public String getScheme() { - return scheme; - } - - public String getHost() { - return host; - } - - public int getPort() { - return port; - } - - public String getPath() { - return path; - } - - public String getAuthorizationHeader() { - return authorizationHeader; - } - - public void setAuthorizationHeader(String header) { - authorizationHeader = header; - } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SPARQL11Request.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SPARQL11Request.java new file mode 100644 index 00000000..d55ae8d3 --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SPARQL11Request.java @@ -0,0 +1,55 @@ +package it.unibo.arces.wot.sepa.commons.request; + +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; + +public abstract class SPARQL11Request extends Request { + protected HTTPMethod method = HTTPMethod.POST; + protected String scheme = null; + protected String host = null; + protected int port = -1; + protected String path = null; + + protected String default_graph_uri = null; + protected String named_graph_uri = null; + + public SPARQL11Request(String sparql, String auth,String defaultGraphUri,String namedGraphUri,long timeout) { + super(sparql, auth,timeout); + + this.default_graph_uri = defaultGraphUri; + this.named_graph_uri = namedGraphUri; + } + + @Override + public String toString() { + return sparql; + + } + + public HTTPMethod getHttpMethod() { + return method; + } + + public String getScheme() { + return scheme; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getPath() { + return path; + } + + public String getDefaultGraphUri() { + return default_graph_uri; + } + + public String getNamedGraphUri() { + return named_graph_uri; + } +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java index 99277150..4bd968aa 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/SubscribeRequest.java @@ -20,32 +20,25 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; - -// TODO: Auto-generated Javadoc /** * This class represents the request of performing a SPARQL 1.1 Subscribe */ -public class SubscribeRequest extends QueryRequest { +public class SubscribeRequest extends Request { /** The alias. */ private String alias = null; + protected String default_graph_uri = null; + protected String named_graph_uri = null; + public SubscribeRequest(String sparql, String alias, String defaultGraphURI, String namedGraphURI, - String authorization) { - this(-1, sparql, alias, defaultGraphURI, namedGraphURI, authorization); - } - - public SubscribeRequest(Integer token, String sparql, String alias, String defaultGraphURI, String namedGraphURI, - String authorization) { - super(token, sparql); + String authorization,long timeout) { + super(sparql, authorization,timeout); - this.alias = alias; - - default_graph_uri = defaultGraphURI; - named_graph_uri = namedGraphURI; - authorizationHeader = authorization; + this.alias = alias; + this.default_graph_uri = defaultGraphURI; + this.named_graph_uri = namedGraphURI; } @Override @@ -58,12 +51,13 @@ public String toString() { body.add("authorization", new JsonPrimitive(getAuthorizationHeader())); if (getAlias() != null) body.add("alias", new JsonPrimitive(getAlias())); - if (getDefaultGraphUri() != null) { - body.add("default-graph-uri", new JsonPrimitive(getDefaultGraphUri())); + if (default_graph_uri != null) { + body.add("default-graph-uri", new JsonPrimitive(default_graph_uri)); } - if (getNamedGraphUri() != null) { - body.add("named-graph-uri", new JsonPrimitive(getNamedGraphUri())); + if (named_graph_uri != null) { + body.add("named-graph-uri", new JsonPrimitive(named_graph_uri)); } + body.add("timeout", new JsonPrimitive(getTimeout())); request.add("subscribe", body); @@ -88,9 +82,4 @@ public boolean equals(Object obj) { if (!(obj instanceof SubscribeRequest)) return false; return sparql.equals(((SubscribeRequest)obj).sparql); } - - @Override - public int hashCode() { - return sparql.hashCode(); - } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java index ee386a3f..6e135a1b 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UnsubscribeRequest.java @@ -22,7 +22,6 @@ import it.unibo.arces.wot.sepa.commons.request.Request; -// TODO: Auto-generated Javadoc /** * The Class UnsubscribeRequest. */ @@ -36,26 +35,8 @@ public class UnsubscribeRequest extends Request { * @param subId * the sub id */ - public UnsubscribeRequest(Integer token, String subId,String authorization) { - super(token, subId); - - super.authorizationHeader = authorization; - } - - /** - * Instantiates a new unsubscribe request. - * - * @param subID - * the sub ID - */ - public UnsubscribeRequest(String subID,String authorization) { - super(subID); - - super.authorizationHeader = authorization; - } - - public UnsubscribeRequest(String subID) { - super(subID); + public UnsubscribeRequest(String subId,String authorization,long timeout) { + super(subId, authorization,timeout); } /** @@ -75,6 +56,9 @@ public String toString() { body.add("spuid", new JsonPrimitive(getSubscribeUUID())); if (getAuthorizationHeader() != null) body.add("authorization", new JsonPrimitive(getAuthorizationHeader())); + + body.add("timeout", new JsonPrimitive(getTimeout())); + request.add("unsubscribe", body); return request.toString(); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java index 1b9e9e66..b096d6d7 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/request/UpdateRequest.java @@ -19,76 +19,26 @@ package it.unibo.arces.wot.sepa.commons.request; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; -import it.unibo.arces.wot.sepa.commons.request.Request; -// TODO: Auto-generated Javadoc /** * This class represents the request to perform a SPARQL 1.1 Update * */ -public class UpdateRequest extends Request { +public class UpdateRequest extends SPARQL11Request { /* It is an error to supply the using-graph-uri or using-named-graph-uri * parameters when using this protocol to convey a SPARQL 1.1 Update request * that contains an operation that uses the USING, USING NAMED, or WITH clause. */ - private String using_graph_uri = null; - private String using_named_graph_uri = null; - /** - * Instantiates a new update request. - * - * @param token the token - * @param sparql the sparql - */ - public UpdateRequest(Integer token, String sparql) { - super(token, sparql); - } - - /** - * Instantiates a new update request. - * - * @param sparql the sparql - */ - public UpdateRequest(String sparql) { - super(sparql); - } - - public UpdateRequest(Integer token,HTTPMethod method,String scheme,String host, int port, String path,String sparql,long timeout,String using_graph_uri,String using_named_graph_uri,String authorization) { - super(token,sparql); - - super.method = method; - super.host = host; - super.port = port; - super.path = path; - super.timeout = timeout; - super.scheme = scheme; - - super.authorizationHeader = authorization; - - this.using_graph_uri = using_graph_uri; - this.using_named_graph_uri = using_named_graph_uri; - + public UpdateRequest(HTTPMethod method,String scheme,String host, int port, String path,String sparql,String default_graph_uri,String named_graph_uri,String authorization,long timeout) { + super(sparql,authorization,default_graph_uri,named_graph_uri,timeout); - } - - public UpdateRequest(String sparql,String using_graph_uri,String using_named_graph_uri) { - super(sparql); - - this.using_graph_uri = using_graph_uri; - this.using_named_graph_uri = using_named_graph_uri; - } - - public UpdateRequest(HTTPMethod updateMethod, String updateProtocolScheme, String updateHost, int updatePort, - String updatePath, String sparql, int timeout, String usingGraphURI, String usingNamedGraphURI,String authorization) { - this(-1,updateMethod, updateProtocolScheme, updateHost, updatePort,updatePath, sparql, timeout, usingGraphURI, usingNamedGraphURI,authorization); - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - if (token != -1) return "UPDATE #"+token+" "+sparql; - return sparql; + this.method = method; + this.host = host; + this.port = port; + this.path = path; + this.timeout = timeout; + this.scheme = scheme; } /* SPARQL Update requests are executed against a Graph Store, a mutable @@ -99,14 +49,6 @@ public String toString() { * USING, USING NAMED, and/or WITH keywords, or it may be specified via the * using-graph-uri and using-named-graph-uri parameters. */ - - public String getUsingGraphUri() { - return using_graph_uri; - } - - public String getUsingNamedGraphUri() { - return using_named_graph_uri; - } public String getAcceptHeader() { return "application/json"; @@ -121,9 +63,4 @@ public boolean equals(Object obj) { if (!(obj instanceof UpdateRequest)) return false; return sparql.equals(((UpdateRequest)obj).sparql); } - - @Override - public int hashCode() { - return sparql.hashCode(); - } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java index ffdaf700..c1dc5ad6 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java @@ -60,23 +60,6 @@ *
    Body is optional * */ public class ErrorResponse extends Response { - /** - * Instantiates a new error response. - * - * @param token the token - * @param code the code - * @param message the message - */ - public ErrorResponse(int token,int code,String message) { - super(token); - - JsonObject body = new JsonObject(); - if (message != null) body.add("body", new JsonPrimitive(message)); - body.add("code", new JsonPrimitive(code)); - - json.add("error", body); - } - /** * Instantiates a new error response. * @@ -92,33 +75,9 @@ public ErrorResponse(int code,String message) { json.add("error", body); } - - /** - * Instantiates a new error response. - * - * @param code the code - */ - public ErrorResponse(int code) { - super(); - - JsonObject body = new JsonObject(); - body.add("code", new JsonPrimitive(code)); - - json.add("error", body); - } - - public ErrorResponse(JsonObject notify) { - json = notify; - } - - public ErrorResponse(int token,JsonObject notify) { - super(token); - json = notify; - } - public ErrorResponse(ErrorResponse ret) { - super(ret.getToken()); - json = ret.json; + public ErrorResponse(JsonObject jsonMessage) { + json = jsonMessage; } /** diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java index 6d876d64..78fa7ed3 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java @@ -47,7 +47,7 @@ public class JWTResponse extends Response { * the expiring */ public JWTResponse(String access_token, String token_type, long expiring) { - super(0); + super(); JsonObject jwt = new JsonObject(); if (access_token != null) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java index d3360a9e..0bf50c89 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Notification.java @@ -23,7 +23,6 @@ import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -// TODO: Auto-generated Javadoc /** * This class represents a SPARQL Notification (see SPARQL 1.1 Subscription * Language) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java index b58987a2..d782aea9 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/QueryResponse.java @@ -27,21 +27,6 @@ */ public class QueryResponse extends Response { - - /** - * Instantiates a new query response. - * - * @param token - * the token - * @param body - * the body - */ - public QueryResponse(Integer token, JsonObject bindingResultsJSON) { - super(token); - json = bindingResultsJSON; - - } - /** * Instantiates a new query response. * @@ -53,11 +38,6 @@ public QueryResponse(JsonObject bindingResultsJSON) { json = bindingResultsJSON; } - public QueryResponse(QueryResponse ret) { - super(ret.getToken()); - json = ret.json; - } - /** * Gets the bindings results. * diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java index 657e6049..7aa54d00 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java @@ -19,7 +19,6 @@ import com.google.gson.JsonObject; -// TODO: Auto-generated Javadoc /** * This class represents the response to a generic request. * */ @@ -28,9 +27,6 @@ public abstract class Response { /** The json. */ protected JsonObject json; - - /** The token. */ - private int token = -1; public boolean isError() { return this.getClass().equals(ErrorResponse.class); @@ -56,15 +52,6 @@ public boolean isUnsubscribeResponse() { public boolean isUpdateResponse() { return this.getClass().equals(UpdateResponse.class); } - /** - * Instantiates a new response. - * - * @param token the token - */ - public Response(Integer token) { - this.token = token; - json = new JsonObject(); - } /** * Instantiates a new response. @@ -73,29 +60,8 @@ public Response() { json = new JsonObject(); } - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { return json.toString(); } - - /** - * Gets the token. - * - * @return the token - */ - public int getToken() { - return token; - } - - /** - * Gets the as json object. - * - * @return the as json object - */ - /*public JsonObject getAsJsonObject(){ - return json; - }*/ } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java index 5f2bff1c..8f7e52fc 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/SubscribeResponse.java @@ -34,38 +34,9 @@ public class SubscribeResponse extends Response { - public SubscribeResponse(JsonObject response){ - json = response; - } - public BindingsResults getBindingsResults() { return new BindingsResults(json.get("notification").getAsJsonObject().get("addedResults").getAsJsonObject()); } - - /** - * Instantiates a new subscribe response. - * - * @param token - * the token - * @param spuid - * the spuid - */ - public SubscribeResponse(Integer token, String spuid,BindingsResults firstResults) { - super(token); - - JsonObject response = new JsonObject(); - - if (spuid != null) - response.add("spuid", new JsonPrimitive(spuid)); - - response.add("sequence", new JsonPrimitive(0)); - - response.add("addedResults", new BindingsResults(firstResults).toJson()); - if (firstResults != null) response.add("removedResults", new BindingsResults(firstResults.getVariables(),null).toJson()); - else response.add("removedResults", new JsonObject()); - - json.add("notification", response); - } /** * Instantiates a new subscribe response. @@ -77,13 +48,14 @@ public SubscribeResponse(Integer token, String spuid,BindingsResults firstResult * @param alias * the alias */ - public SubscribeResponse(Integer token, String spuid, String alias,BindingsResults firstResults) { - super(token); + public SubscribeResponse(String spuid, String alias,BindingsResults firstResults) { + super(); JsonObject response = new JsonObject(); - if (spuid != null) - response.add("spuid", new JsonPrimitive(spuid)); + if (spuid == null) throw new IllegalArgumentException("SPUID is null"); + response.add("spuid", new JsonPrimitive(spuid)); + if (alias != null) response.add("alias", new JsonPrimitive(alias)); @@ -96,59 +68,8 @@ public SubscribeResponse(Integer token, String spuid, String alias,BindingsResul json.add("notification", response); } - /** - * Instantiates a new subscribe response. - * - * @param spuid - * the spuid - */ - public SubscribeResponse(String spuid) { - super(); - - JsonObject response = new JsonObject(); - - if (spuid != null) - response.add("spuid", new JsonPrimitive(spuid)); - - response.add("sequence", new JsonPrimitive(0)); - - response.add("addedResults", new JsonObject()); - response.add("removedResults", new JsonObject()); - - json.add("notification", response); - } - - /** - * Instantiates a new subscribe response. - */ - public SubscribeResponse() { - super(); - } - - /** - * Instantiates a new subscribe response. - * - * @param spuid - * the spuid - * @param alias - * the alias - */ - public SubscribeResponse(String spuid, String alias) { - super(); - - JsonObject response = new JsonObject(); - - if (spuid != null) - response.add("spuid", new JsonPrimitive(spuid)); - if (alias != null) - response.add("alias", new JsonPrimitive(alias)); - - response.add("sequence", new JsonPrimitive(0)); - - response.add("addedResults", new JsonObject()); - response.add("removedResults", new JsonObject()); - - json.add("notification", response); + public SubscribeResponse(JsonObject jsonMessage) { + json = jsonMessage; } /** diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java index fd66e95c..c8455332 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UnsubscribeResponse.java @@ -35,25 +35,6 @@ public class UnsubscribeResponse extends Response { - /** - * Instantiates a new unsubscribe response. - * - * @param token - * the token - * @param spuid - * the spuid - */ - public UnsubscribeResponse(Integer token, String spuid) { - super(token); - - JsonObject response = new JsonObject(); - - if (spuid != null) - response.add("spuid", new JsonPrimitive(spuid)); - - json.add("unsubscribed", response); - } - /** * Instantiates a new unsubscribe response. * @@ -71,15 +52,8 @@ public UnsubscribeResponse(String spuid) { json.add("unsubscribed", response); } - /** - * Instantiates a new unsubscribe response. - */ - public UnsubscribeResponse() { - super(); - } - - public UnsubscribeResponse(JsonObject notify) { - json = notify; + public UnsubscribeResponse(JsonObject jsonMessage) { + json = jsonMessage; } /** diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java index ad264e50..f53ffd5a 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/UpdateResponse.java @@ -23,39 +23,11 @@ import it.unibo.arces.wot.sepa.commons.response.Response; -// TODO: Auto-generated Javadoc /** * This class represents the response of a SPARQL 1.1 Update */ public class UpdateResponse extends Response { - - /** - * Instantiates a new update response. - * - * @param token - * the token - * @param body - * the body - */ - public UpdateResponse(Integer token, String body) { - super(token); - - try { - JsonObject jbody = new JsonParser().parse(body).getAsJsonObject(); - - json.add("updateResponse", new JsonObject()); - json.get("updateResponse").getAsJsonObject().add("body", jbody); - json.get("updateResponse").getAsJsonObject().add("isJsonBody", new JsonPrimitive(true)); - - } - catch(Exception e) { - json.add("updateResponse", new JsonObject()); - json.get("updateResponse").getAsJsonObject().add("body", new JsonPrimitive(body)); - json.get("updateResponse").getAsJsonObject().add("isJsonBody", new JsonPrimitive(false)); - } - } - /** * Instantiates a new update response. * @@ -79,9 +51,4 @@ public UpdateResponse(String body) { json.get("response").getAsJsonObject().add("isJson", new JsonPrimitive(false)); } } - - public UpdateResponse(UpdateResponse ret) { - super(ret.getToken()); - json = ret.json; - } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java index e0bed4b6..9a0dc134 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java @@ -48,14 +48,18 @@ public class AuthenticationProperties { private static final Logger logger = LogManager.getLogger(); /** The properties file. */ - protected File propertiesFile; + protected File propertiesFile = null; - private Encryption encryption; + private Encryption encryption = null; - protected JsonObject jsap; + protected JsonObject jsap = null; - private String registrationURL; - private String tokenRequestURL; + private String registrationURL = null; + private String tokenRequestURL = null; + + public AuthenticationProperties() { + + } public AuthenticationProperties(String jsapFileName, byte[] secret) throws SEPAPropertiesException { FileReader in; diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java index 0ec10bf9..e5b08eef 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java @@ -170,7 +170,7 @@ public class SEPASecurityManager implements HostnameVerifier { * the key password * @throws SEPASecurityException */ - private void _SEPASecurityManager(String jksName, String jksPassword, String keyPassword, + public SEPASecurityManager(String jksName, String jksPassword, String keyPassword, AuthenticationProperties oauthProp) throws SEPASecurityException { // Arguments check if (jksName == null || jksPassword == null) @@ -207,27 +207,30 @@ private void _SEPASecurityManager(String jksName, String jksPassword, String key } public SEPASecurityManager(AuthenticationProperties oauthProp) throws SEPASecurityException { - _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", oauthProp); - - if (oauthProp == null) - throw new IllegalArgumentException("Authorization properties are null"); - } - - public SEPASecurityManager() throws SEPASecurityException { - _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); + this("sepa.jks", "sepa2017", "sepa2017", oauthProp); } - + public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { - _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); - } - - public SEPASecurityManager(String jksName, String jksPassword, String keyPassword, - AuthenticationProperties oauthProp) throws SEPASecurityException { - _SEPASecurityManager(jksName, "sepa2017", "sepa2017", oauthProp); - - if (oauthProp == null) - throw new IllegalArgumentException("Authorization properties are null"); - } + this(jksName,jksPassword,keyPassword, new AuthenticationProperties()); + } +// if (oauthProp == null) +// throw new IllegalArgumentException("Authorization properties are null"); + +// public SEPASecurityManager() throws SEPASecurityException { +// _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); +// } +// +// public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { +// _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); +// } + +// public SEPASecurityManager(String jksName, String jksPassword, String keyPassword, +// AuthenticationProperties oauthProp) throws SEPASecurityException { +// _SEPASecurityManager(jksName, "sepa2017", "sepa2017", oauthProp); +// +// if (oauthProp == null) +// throw new IllegalArgumentException("Authorization properties are null"); +// } public Socket getSSLSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); @@ -288,7 +291,7 @@ public String getAuthorizationHeader() throws SEPAPropertiesException, SEPASecur /** * Request registration of "entity" at the Authorization Server listening at the specified URL * */ - public Response register(String url, String identity) { + private Response register(String url, String identity) { logger.debug("REGISTER " + identity); CloseableHttpResponse response = null; @@ -314,7 +317,7 @@ public Response register(String url, String identity) { if (json.has("error")) { Timings.log("REGISTER_ERROR", start, Timings.getTime()); - ErrorResponse error = new ErrorResponse(0, json.get("error").getAsJsonObject().get("code").getAsInt(), + ErrorResponse error = new ErrorResponse(json.get("error").getAsJsonObject().get("code").getAsInt(), json.get("error").getAsJsonObject().get("body").getAsString()); logger.error(error); return error; @@ -358,7 +361,7 @@ public Response register(String url, String identity) { * @see JWTResponse */ - public Response requestToken(String url, String authorization) { + private Response requestToken(String url, String authorization) { logger.debug("TOKEN_REQUEST"); CloseableHttpResponse response = null; @@ -390,7 +393,7 @@ public Response requestToken(String url, String authorization) { return new JWTResponse(oauthProperties.getToken(),oauthProperties.getTokenType(),oauthProperties.getExpiringSeconds()); } - ErrorResponse error = new ErrorResponse(0, json.get("error").getAsJsonObject().get("code").getAsInt(), + ErrorResponse error = new ErrorResponse(json.get("error").getAsJsonObject().get("code").getAsInt(), json.get("error").getAsJsonObject().get("body").getAsString()); logger.error(error); return error; diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java index 47a34db5..e788a4eb 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Aggregator.java @@ -20,7 +20,6 @@ import java.io.IOException; -import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,7 +29,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -90,19 +88,20 @@ public final Response update(int timeout) throws SEPASecurityException, IOExcept String authorizationHeader = null; if (isSecure()) { - if (!getToken()) - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); - - if (appProfile.getAuthenticationProperties() != null) - authorizationHeader = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); + authorizationHeader = sm.getAuthorizationHeader(); +// if (!getToken()) +// return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); +// +// if (appProfile.getAuthenticationProperties() != null) +// authorizationHeader = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); } UpdateRequest req = new UpdateRequest(appProfile.getUpdateMethod(SPARQL_ID), appProfile.getUpdateProtocolScheme(SPARQL_ID), appProfile.getUpdateHost(SPARQL_ID), appProfile.getUpdatePort(SPARQL_ID), appProfile.getUpdatePath(SPARQL_ID), - prefixes() + replaceBindings(sparqlUpdate, updateForcedBindings), timeout, + prefixes() + replaceBindings(sparqlUpdate, updateForcedBindings), appProfile.getUsingGraphURI(SPARQL_ID), appProfile.getUsingNamedGraphURI(SPARQL_ID), - authorizationHeader); + authorizationHeader,timeout); return client.update(req); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java index 7417736e..c87ccb65 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Client.java @@ -18,21 +18,15 @@ package it.unibo.arces.wot.sepa.pattern; -import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.Date; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.JWTResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; @@ -246,27 +240,27 @@ private boolean isValidVarChar(int c) { || (0xF900 <= c && c <= 0xFDCF) || (0xFDF0 <= c && c <= 0xFFFD) || (0x10000 <= c && c <= 0xEFFFF)); } - protected boolean getToken() throws SEPASecurityException, IOException, SEPAPropertiesException { - try { - if (!appProfile.getAuthenticationProperties().isTokenExpired()) - return true; - } catch (Exception e) { - logger.error("Authentication properties not found"); - throw new SEPASecurityException(new NullPointerException("Authentication properties not found")); - } - - Response ret = sm.requestToken(appProfile.getAuthenticationProperties().getTokenRequestUrl(), - appProfile.getAuthenticationProperties().getBasicAuthorizationHeader()); - - if (ret.isJWTResponse()) { - JWTResponse token = (JWTResponse) ret; - Date expires = new Date(); - expires.setTime(expires.getTime() + (1000 * token.getExpiresIn())); - appProfile.getAuthenticationProperties().setJWT(token.getAccessToken(), expires, token.getTokenType()); - return true; - } - - logger.error(ret); - return false; - } +// protected boolean getToken() throws SEPASecurityException, IOException, SEPAPropertiesException { +// try { +// if (!appProfile.getAuthenticationProperties().isTokenExpired()) +// return true; +// } catch (Exception e) { +// logger.error("Authentication properties not found"); +// throw new SEPASecurityException(new NullPointerException("Authentication properties not found")); +// } +// +// Response ret = sm.requestToken(appProfile.getAuthenticationProperties().getTokenRequestUrl(), +// appProfile.getAuthenticationProperties().getBasicAuthorizationHeader()); +// +// if (ret.isJWTResponse()) { +// JWTResponse token = (JWTResponse) ret; +// Date expires = new Date(); +// expires.setTime(expires.getTime() + (1000 * token.getExpiresIn())); +// appProfile.getAuthenticationProperties().setJWT(token.getAccessToken(), expires, token.getTokenType()); +// return true; +// } +// +// logger.error(ret); +// return false; +// } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java index 12ef0c8e..565d229c 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java @@ -20,7 +20,6 @@ import java.io.IOException; -import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -28,19 +27,15 @@ import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.RDFTerm; -import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; -import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; public abstract class Consumer extends Client implements IConsumer { @@ -76,16 +71,12 @@ public Consumer(JSAP appProfile, String subscribeID, SEPASecurityManager sm) throw new SEPAProtocolException(new IllegalArgumentException("SPARQL subscribe is null")); } - ISubscriptionProtocol protocol = null; + SubscriptionProtocol protocol = null; - if (appProfile.getSubscribeProtocol(subscribeID).equals(SubscriptionProtocol.WS)) { - throw new SEPAProtocolException(new IllegalArgumentException("Wrong constructor. Use the unsecure one")); - } - - protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), - appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID),sm); + protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), + appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID),sm,this); - client = new SPARQL11SEProtocol(protocol, this); + client = new SPARQL11SEProtocol(protocol); subID = subscribeID; } @@ -113,16 +104,12 @@ public Consumer(JSAP appProfile, String subscribeID) throws SEPAProtocolExceptio throw new SEPAProtocolException(new IllegalArgumentException("SPARQL subscribe is null")); } - ISubscriptionProtocol protocol = null; + SubscriptionProtocol protocol = null; - if (appProfile.getSubscribeProtocol(subscribeID).equals(SubscriptionProtocol.WSS)) { - throw new SEPAProtocolException(new IllegalArgumentException("Missing security parameters")); - } + protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), + appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID),this); - protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), - appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID)); - - client = new SPARQL11SEProtocol(protocol, this); + client = new SPARQL11SEProtocol(protocol); subID = subscribeID; } @@ -132,39 +119,26 @@ public final void setSubscribeBindingValue(String variable, RDFTerm value) throw } - public final Response subscribe() throws SEPASecurityException, IOException, SEPAPropertiesException { + public final void subscribe(long timeout) throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { String authorizationHeader = null; - if (isSecure()) { - if (!getToken()) - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); - if (appProfile.getAuthenticationProperties()!= null) - authorizationHeader = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); - } + if (isSecure()) authorizationHeader = sm.getAuthorizationHeader(); String sparql = prefixes() + replaceBindings(sparqlSubscribe, forcedBindings); - Response response = client.subscribe(new SubscribeRequest(sparql, null, appProfile.getDefaultGraphURI(subID), + client.subscribe(new SubscribeRequest(sparql, null, appProfile.getDefaultGraphURI(subID), appProfile.getNamedGraphURI(subID), - authorizationHeader)); - - if (response.isSubscribeResponse()) { - subID = ((SubscribeResponse) response).getSpuid(); - } - - return response; + authorizationHeader,timeout)); } - public final Response unsubscribe() throws SEPASecurityException, IOException, SEPAPropertiesException { + public final void unsubscribe(long timeout) throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { logger.debug("UNSUBSCRIBE " + subID); - if (isSecure()) { - if (!getToken()) - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); - } + String oauth = null; + if (isSecure()) oauth = sm.getAuthorizationHeader(); - return client.unsubscribe( - new UnsubscribeRequest(subID, appProfile.getAuthenticationProperties().getBearerAuthorizationHeader())); + client.unsubscribe( + new UnsubscribeRequest(subID, oauth,timeout)); } @Override diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java index e1e8c2cc..4cbf1269 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java @@ -19,14 +19,13 @@ package it.unibo.arces.wot.sepa.pattern; import java.io.IOException; +import java.net.URISyntaxException; import java.util.Hashtable; -import org.apache.http.HttpStatus; - import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.ISubscriptionProtocol; -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; +import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; @@ -36,10 +35,8 @@ import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.RegistrationResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; public class GenericClient extends Client { @@ -71,45 +68,47 @@ public Response query(String ID, String sparql, Bindings forced, int timeout) } public Response query(String ID, Bindings forced, int timeout) - throws SEPAProtocolException, SEPASecurityException, IOException, SEPAPropertiesException { - return _query(ID, null, forced, timeout); + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + try { + return _query(ID, null, forced, timeout); + } catch (IOException e) { + throw new SEPAProtocolException(e); + } } - public Response subscribe(String ID, String sparql, Bindings forced, ISubscriptionHandler handler) - throws SEPAProtocolException, SEPASecurityException, IOException, SEPAPropertiesException { - return _subscribe(ID, sparql, forced, handler); + public void subscribe(String ID, String sparql, Bindings forced, ISubscriptionHandler handler,long timeout) + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + try { + _subscribe(ID, sparql, forced, handler,timeout); + } catch (IOException | URISyntaxException e) { + throw new SEPAProtocolException(e); + } } - public Response subscribe(String ID, Bindings forced, ISubscriptionHandler handler) - throws SEPAProtocolException, SEPASecurityException, IOException, SEPAPropertiesException { - return _subscribe(ID, null, forced, handler); + public void subscribe(String ID, Bindings forced, ISubscriptionHandler handler,long timeout) + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + try { + _subscribe(ID, null, forced, handler,timeout); + } catch (IOException | URISyntaxException e) { + throw new SEPAProtocolException(e); + } } - public Response unsubscribe(String subID) throws SEPASecurityException, IOException, SEPAPropertiesException { - if (!subscriptions.containsKey(subID)) - return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, subID + " not present"); + public void unsubscribe(String subID,long timeout) throws SEPASecurityException, SEPAPropertiesException, SEPAProtocolException { + if (!subscriptions.containsKey(subID)) return; String clientURL = subscriptions.get(subID); String auth = null; - if (subscribedClients.get(clientURL).isSecure()) { - if (!getToken()) - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); - try { - auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); - } catch (Exception e) { - } - } - - Response ret = subscribedClients.get(clientURL).unsubscribe(new UnsubscribeRequest(subID, auth)); + if (subscribedClients.get(clientURL).isSecure()) auth = sm.getAuthorizationHeader(); - if (ret.isSubscribeResponse()) { - subscriptions.values().remove(subID); - if (!subscriptions.values().contains(clientURL)) - subscribedClients.remove(clientURL); - } + subscribedClients.get(clientURL).unsubscribe(new UnsubscribeRequest(subID, auth,timeout)); - return ret; +// if (ret.isSubscribeResponse()) { +// subscriptions.values().remove(subID); +// if (!subscriptions.values().contains(clientURL)) +// subscribedClients.remove(clientURL); +// } } private Response _update(String ID, String sparql, Bindings forced, int timeout) @@ -119,13 +118,14 @@ private Response _update(String ID, String sparql, Bindings forced, int timeout) String auth = null; if (isSecure()) { client = new SPARQL11Protocol(sm); - if (!getToken()) { - client.close(); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); - } +// if (!getToken()) { +// client.close(); +// return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); +// } try { - auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); +// auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); + auth = sm.getAuthorizationHeader(); } catch (Exception e) { } } else @@ -138,8 +138,8 @@ private Response _update(String ID, String sparql, Bindings forced, int timeout) sparql = appProfile.getSPARQLUpdate(ID); Response ret = client.update(new UpdateRequest(appProfile.getUpdateMethod(ID), appProfile.getUpdateProtocolScheme(ID), appProfile.getUpdateHost(ID), appProfile.getUpdatePort(ID), - appProfile.getUpdatePath(ID), prefixes() + replaceBindings(sparql, forced), timeout, - appProfile.getUsingGraphURI(ID), appProfile.getUsingNamedGraphURI(ID), auth)); + appProfile.getUpdatePath(ID), prefixes() + replaceBindings(sparql, forced), + appProfile.getUsingGraphURI(ID), appProfile.getUsingNamedGraphURI(ID), auth,timeout)); client.close(); return ret; @@ -152,12 +152,13 @@ private Response _query(String ID, String sparql, Bindings forced, int timeout) String auth = null; if (isSecure()) { client = new SPARQL11Protocol(sm); - if (!getToken()) { - client.close(); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); - } +// if (!getToken()) { +// client.close(); +// return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); +// } try { - auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); +// auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); + auth = sm.getAuthorizationHeader(); } catch (Exception e) { } } else @@ -167,19 +168,19 @@ private Response _query(String ID, String sparql, Bindings forced, int timeout) sparql = appProfile.getSPARQLQuery(ID); Response ret = client.query(new QueryRequest(appProfile.getQueryMethod(ID), appProfile.getQueryProtocolScheme(ID), appProfile.getQueryHost(ID), appProfile.getQueryPort(ID), - appProfile.getQueryPath(ID), prefixes() + replaceBindings(sparql, forced), timeout, - appProfile.getDefaultGraphURI(ID), appProfile.getNamedGraphURI(ID), auth)); + appProfile.getQueryPath(ID), prefixes() + replaceBindings(sparql, forced), + appProfile.getDefaultGraphURI(ID), appProfile.getNamedGraphURI(ID), auth,timeout)); client.close(); return ret; } - private Response _subscribe(String ID, String sparql, Bindings forced, ISubscriptionHandler handler) - throws SEPAProtocolException, SEPASecurityException, IOException, SEPAPropertiesException { + private void _subscribe(String ID, String sparql, Bindings forced, ISubscriptionHandler handler,long timeout) + throws SEPAProtocolException, SEPASecurityException, IOException, SEPAPropertiesException, URISyntaxException { // Create client String url = null; - ISubscriptionProtocol protocol = null; + SubscriptionProtocol protocol = null; String auth = null; SPARQL11SEProtocol client = null; @@ -189,10 +190,10 @@ private Response _subscribe(String ID, String sparql, Bindings forced, ISubscrip + appProfile.getSubscribePath(ID); if (!subscribedClients.containsKey(url)) { - protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), - appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID)); + protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), + appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID),handler); - client = new SPARQL11SEProtocol(protocol, handler); + client = new SPARQL11SEProtocol(protocol); } else client = subscribedClients.get(url); @@ -202,20 +203,21 @@ private Response _subscribe(String ID, String sparql, Bindings forced, ISubscrip + appProfile.getSubscribePath(ID); if (!subscribedClients.containsKey(url)) { - protocol = new WebSocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), - appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID), sm); + protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), + appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID), sm,handler); - client = new SPARQL11SEProtocol(protocol, handler, sm); + client = new SPARQL11SEProtocol(protocol, sm); } else client = subscribedClients.get(url); - if (!getToken()) - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); +// if (!getToken()) +// return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get or renew token"); try { - auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); +// auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); + auth = sm.getAuthorizationHeader(); } catch (Exception e) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Failed to get bearer authorization header"); + throw new SEPASecurityException("Failed to get bearer authorization header"); } break; @@ -226,19 +228,19 @@ private Response _subscribe(String ID, String sparql, Bindings forced, ISubscrip sparql = appProfile.getSPARQLQuery(ID); SubscribeRequest req = new SubscribeRequest(prefixes() + replaceBindings(sparql, forced), null, - appProfile.getDefaultGraphURI(ID), appProfile.getNamedGraphURI(ID), auth); + appProfile.getDefaultGraphURI(ID), appProfile.getNamedGraphURI(ID), auth,timeout); - Response ret = client.subscribe(req); + client.subscribe(req); // Parse response - if (ret.isSubscribeResponse()) { - String spuid = ((SubscribeResponse) ret).getSpuid(); - if (!subscribedClients.containsKey(url)) - subscribedClients.put(url, client); - subscriptions.put(spuid, url); - } - - return ret; +// if (ret.isSubscribeResponse()) { +// String spuid = ((SubscribeResponse) ret).getSpuid(); +// if (!subscribedClients.containsKey(url)) +// subscribedClients.put(url, client); +// subscriptions.put(spuid, url); +// } +// +// return ret; } @Override @@ -249,9 +251,9 @@ public void close() throws IOException { // Registration to the Authorization Server (AS) public Response register(String identity) throws SEPASecurityException, SEPAPropertiesException { - SEPASecurityManager security = new SEPASecurityManager(); + SEPASecurityManager security = new SEPASecurityManager(appProfile.getAuthenticationProperties()); - Response ret = security.register(appProfile.getAuthenticationProperties().getRegisterUrl(), identity); + Response ret = security.register(identity); if (ret.isRegistrationResponse()) { RegistrationResponse registration = (RegistrationResponse) ret; diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java index 90d29fbb..cae4fc0b 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IConsumer.java @@ -22,14 +22,14 @@ import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; public interface IConsumer extends ISubscriptionHandler { - Response subscribe() throws SEPASecurityException, IOException, SEPAPropertiesException ; - Response unsubscribe() throws SEPASecurityException, IOException, SEPAPropertiesException; + void subscribe(long timeout) throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException ; + void unsubscribe(long timeout) throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException; void onResults(ARBindingsResults results); void onAddedResults(BindingsResults results); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java index 5e056e11..45bd5d23 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/IProducer.java @@ -25,6 +25,5 @@ import it.unibo.arces.wot.sepa.commons.response.Response; public interface IProducer { - public Response update() throws SEPASecurityException, IOException, SEPAPropertiesException; public Response update(int timeout) throws SEPASecurityException, IOException, SEPAPropertiesException; } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java index 7746de74..0c95b876 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Producer.java @@ -20,7 +20,6 @@ import java.io.IOException; -import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -31,8 +30,8 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; public class Producer extends Client implements IProducer { @@ -44,7 +43,7 @@ public class Producer extends Client implements IProducer { private SPARQL11Protocol client; - public Producer(JSAP appProfile,String updateID) throws SEPAProtocolException, SEPASecurityException { + public Producer(JSAP appProfile,String updateID) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(appProfile); if (appProfile.getSPARQLUpdate(updateID) == null) { @@ -59,7 +58,7 @@ public Producer(JSAP appProfile,String updateID) throws SEPAProtocolException, S forcedBindings = appProfile.getUpdateBindings(updateID); if (appProfile.getUpdateProtocolScheme(updateID).equals("https")) { - SEPASecurityManager sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017"); + SEPASecurityManager sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", new AuthenticationProperties(appProfile.getFilename())); client = new SPARQL11Protocol(sm); } else client = new SPARQL11Protocol(); @@ -73,14 +72,15 @@ public final Response update(int timeout) throws SEPASecurityException, IOExcept String authorizationHeader = null; if (isSecure()) { - if(!getToken()) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Failed to get or renew token"); - if (appProfile.getAuthenticationProperties()!= null) - authorizationHeader = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); + authorizationHeader = sm.getAuthorizationHeader(); +// if(!getToken()) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Failed to get or renew token"); +// if (appProfile.getAuthenticationProperties()!= null) +// authorizationHeader = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); } UpdateRequest req = new UpdateRequest(appProfile.getUpdateMethod(SPARQL_ID), appProfile.getUpdateProtocolScheme(SPARQL_ID),appProfile.getUpdateHost(SPARQL_ID), appProfile.getUpdatePort(SPARQL_ID), - appProfile.getUpdatePath(SPARQL_ID), prefixes() + replaceBindings(sparqlUpdate, forcedBindings), timeout, - appProfile.getUsingGraphURI(SPARQL_ID), appProfile.getUsingNamedGraphURI(SPARQL_ID),authorizationHeader); + appProfile.getUpdatePath(SPARQL_ID), prefixes() + replaceBindings(sparqlUpdate, forcedBindings), + appProfile.getUsingGraphURI(SPARQL_ID), appProfile.getUsingNamedGraphURI(SPARQL_ID),authorizationHeader,timeout); return client.update(req); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java index 1469b6e8..8123c5b2 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java @@ -23,26 +23,26 @@ public synchronized static void log(Request request) { long start = getTime(); String tag; - if (request.isUpdateRequest()) tag = "REQUEST_UPDATE_"; - else if (request.isSubscribeRequest()) tag = "REQUEST_SUBSCRIBE_"; - else if(request.isQueryRequest()) tag = "REQUEST_QUERY_"; - else if(request.isUnsubscribeRequest()) tag = "REQUEST_UNSUBSCRIBE_"; - else tag = "REQUEST_UNKNOWN_"; + if (request.isUpdateRequest()) tag = "UPDATE_REQUEST"; + else if (request.isSubscribeRequest()) tag = "SUBSCRIBE_REQUEST"; + else if(request.isQueryRequest()) tag = "QUERY_REQUEST"; + else if(request.isUnsubscribeRequest()) tag = "UNSUBSCRIBE_REQUEST"; + else tag = "UNKNOWN_REQUEST"; - log(tag+request.getToken(),start,start); + log(tag,start,start); } public synchronized static void log(Response response) { long start = getTime(); String tag; - if (response.isUpdateResponse()) tag = "RESPONSE_UPDATE_"; - else if (response.isSubscribeResponse()) tag = "RESPONSE_SUBSCRIBE_"; - else if(response.isQueryResponse()) tag = "RESPONSE_QUERY_"; - else if(response.isUnsubscribeResponse()) tag = "RESPONSE_UNSUBSCRIBE_"; - else if(response.isError()) tag = "RESPONSE_ERROR_"; - else tag = "RESPONSE_UNKNOWN_"; + if (response.isUpdateResponse()) tag = "UPDATE_RESPONSE"; + else if (response.isSubscribeResponse()) tag = "SUBSCRIBE_RESPONSE"; + else if(response.isQueryResponse()) tag = "QUERY_RESPONSE"; + else if(response.isUnsubscribeResponse()) tag = "UNSUBSCRIBE_RESPONSE"; + else if(response.isError()) tag = "ERROR_RESPONSE"; + else tag = "UNKNOWN_RESPONSE"; - log(tag+response.getToken(),start,start); + log(tag,start,start); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index bd1775f6..e98e2da9 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.api; -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -9,14 +9,17 @@ import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.pattern.JSAP; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -24,11 +27,15 @@ import org.junit.Test; import java.io.IOException; +import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; import static org.junit.Assert.*; -public class ITSPARQL11SEProtocol { +public class ITSPARQL11SEProtocol implements ISubscriptionHandler { + protected final Logger logger = LogManager.getLogger(); + protected static JSAP properties = null; private static SEPASecurityManager sm; private static SPARQL11SEProtocol client = null; @@ -36,8 +43,11 @@ public class ITSPARQL11SEProtocol { private final static String VALID_ID = "SEPATest"; private final static String NOT_VALID_ID = "RegisterMePlease"; - private static AtomicLong mutex = new AtomicLong(0); - + private static AtomicLong events = new AtomicLong(0); + private static AtomicLong subscribes = new AtomicLong(0); + private String spuid = null; + private static final Object spuidMutex = new Object(); + @BeforeClass public static void init() throws Exception { properties = ConfigurationProvider.GetTestEnvConfiguration(); @@ -47,9 +57,7 @@ public static void init() throws Exception { new AuthenticationProperties(properties.getFileName())); // Registration - Response response = sm.register(NOT_VALID_ID); - assertFalse("Failed to register a not valid ID", !response.isError()); - response = sm.register(VALID_ID); + Response response = sm.register(VALID_ID); assertFalse("Failed to register a valid ID", response.isError()); } } @@ -60,148 +68,436 @@ public static void dispose() throws IOException { } @Before - public void beginTest() throws IOException, IllegalArgumentException, SEPAProtocolException, - SEPAPropertiesException, SEPASecurityException { - - ISubscriptionProtocol protocol = new WebSocketSubscriptionProtocol(properties.getDefaultHost(),properties.getSubscribePort(),properties.getSubscribePath()); - - if (sm == null) - client = new SPARQL11SEProtocol(protocol, new MockSubscriptionHandler()); - else - client = new SPARQL11SEProtocol(protocol, new MockSubscriptionHandler(), sm); + public void beginTest() throws IOException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, + URISyntaxException { + + SubscriptionProtocol protocol; + + if (sm == null) { + protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), + properties.getSubscribePath(), this); + client = new SPARQL11SEProtocol(protocol); + } else { + protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), + properties.getSubscribePath(), sm, this); + client = new SPARQL11SEProtocol(protocol, sm); + } assertFalse("Failed to create SPARQL11SEProtocol", client == null); + Response ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + + assertFalse("Failed to create Delete all triples", ret.isError()); + + subscribes.set(0); + events.set(0); + } + + @After + public void endTest() throws IOException { + if (client != null) + client.close(); + } + + @Test(timeout = 5000) + public void RegisterNotAllowed() throws SEPASecurityException, SEPAPropertiesException { + if (sm != null) { + Response response = sm.register(NOT_VALID_ID); + assertFalse("Failed to register a not valid ID", !response.isError()); + } + } + + @Test(timeout = 5000) + public void Register() throws SEPASecurityException, SEPAPropertiesException { + if (sm != null) { + Response response = sm.register(VALID_ID); + assertFalse("Failed to register a valid ID", response.isError()); + } + } + + @Test(timeout = 5000) + public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException { // Delete all triples - Response ret = client.update(buildUpdateRequest("DELETE_ALL",5000)); + Response ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); assertFalse(String.valueOf(ret), ret.isError()); // Check that the store size is 0 - ret = client.query(buildQueryRequest("COUNT",5000)); + ret = client.query(buildQueryRequest("COUNT", 5000)); assertFalse(String.valueOf(ret), ret.isError()); + QueryResponse results = (QueryResponse) ret; assertFalse(String.valueOf(results), results.getBindingsResults().size() != 1); + for (Bindings bindings : results.getBindingsResults().getBindings()) { assertFalse(String.valueOf(results), bindings.getValue("n") == null); - assertTrue(String.valueOf(results), bindings.getValue("n").equals("0")); + assertFalse(String.valueOf(results), !bindings.getValue("n").equals("0")); } } - @After - public void endTest() throws IOException { - if (client != null) - client.close(); + @Test(timeout = 15000) + public void GetAuthorizationHeader() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { + if (sm != null) { + sm.register(VALID_ID); + sm.getAuthorizationHeader(); + + // For testing the token expires in 5 seconds + for (int i = 0; i < 10; i++) { + sm.getAuthorizationHeader(); + Thread.sleep(1000); + } + } } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException { - Response ret = client.update(buildUpdateRequest("VAIMEE",5000)); + Response ret = client.update(buildUpdateRequest("VAIMEE", 5000)); assertFalse(String.valueOf(ret), ret.isError()); } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException { - Response ret = client.query(buildQueryRequest("ALL",5000)); + Response ret = client.query(buildQueryRequest("ALL", 5000)); assertFalse(String.valueOf(ret), ret.isError()); } - @Test(timeout = 5000) + @Test(timeout = 1000) public void UpdateAndQuery() throws IOException, SEPAPropertiesException, SEPASecurityException { - Response ret = client.update(buildUpdateRequest("VAIMEE",5000)); + Response ret = client.update(buildUpdateRequest("VAIMEE", 5000)); assertFalse(String.valueOf(ret), ret.isError()); - ret = client.query(buildQueryRequest("ALL",5000)); + ret = client.query(buildQueryRequest("VAIMEE", 5000)); + assertFalse(String.valueOf(ret), ret.isError()); assertFalse(String.valueOf(ret), ((QueryResponse) ret).getBindingsResults().size() != 1); } - @Test(timeout = 5000) - public void Subscribe() throws SEPAPropertiesException, SEPASecurityException { - Response ret = client.subscribe(buildSubscribeRequest("ALL")); - assertFalse(String.valueOf(ret), ret.isError()); + @Test(timeout = 1000) + public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + client.subscribe(buildSubscribeRequest("ALL", 5000)); + + synchronized (subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + + client.close(); + + assertFalse("Failed to subscribe", subscribes.get() == 0); } - - @Test(timeout = 5000) - public void Subscribex10() throws SEPAPropertiesException, SEPASecurityException { - for (int i= 0; i < 10 ; i++) { - Response ret = client.subscribe(buildSubscribeRequest("ALL")); - assertFalse("Failed at: "+i+" "+String.valueOf(ret), ret.isError()); + + @Test(timeout = 1000) + public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + int n = 33; + int total = 3 * n; + + for (int i = 0; i < n; i++) { + client.subscribe(buildSubscribeRequest("ALL", 5000)); + client.subscribe(buildSubscribeRequest("RANDOM", 5000)); + client.subscribe(buildSubscribeRequest("RANDOM1", 5000)); + } + + while (subscribes.get() < total) { + synchronized (subscribes) { + try { + subscribes.wait(2000); + } catch (InterruptedException e) { + + } + } + } + + while (events.get() < total) { + synchronized (events) { + try { + events.wait(2000); + } catch (InterruptedException e) { + + } + } } + + client.close(); + + assertFalse("Failed to subscribe (" + subscribes.get() + " on " + total + ")", subscribes.get() != total); + assertFalse("Failed to receive first results. Received: " + events.get() + " on: " + total + " events", + events.get() != total); } - @Test(timeout = 5000) - public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException { - Response ret = client.subscribe(buildSubscribeRequest("ALL")); - assertFalse(String.valueOf(ret), ret.isError()); + @Test(timeout = 1000) + public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + spuid = null; - ret = client.unsubscribe(buildUnsubscribeRequest(((SubscribeResponse)ret).getSpuid())); - assertFalse(String.valueOf(ret), ret.isError()); + client.subscribe(buildSubscribeRequest("ALL", 5000)); + + while (spuid == null) { + synchronized (spuidMutex) { + try { + spuidMutex.wait(1000); + } catch (InterruptedException e) { + + } + } + } + + while (events.get() < 1) { + synchronized (events) { + try { + events.wait(1000); + } catch (InterruptedException e) { + + } + } + } + + if (spuid != null) { + client.unsubscribe(buildUnsubscribeRequest(spuid, 5000)); + + while (subscribes.get() != 0) { + synchronized (subscribes) { + try { + subscribes.wait(1000); + } catch (InterruptedException e) { + + } + } + } + } + + client.close(); + + assertFalse("Failed to subscribe", spuid == null); + assertFalse("Received: " + events.get(), events.get() != 1); + assertFalse("Failed to unsubscribe " + subscribes.get(), subscribes.get() != 0); } - @Test(timeout = 5000) - public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, - SEPAPropertiesException, SEPASecurityException { + @Test(timeout = 1000) + public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, + SEPASecurityException, InterruptedException { - new Subscriber("ALL",properties,sm,mutex).start(); + Subscriber sub = new Subscriber("VAIMEE", properties, sm, this); + sub.start(); - Thread.sleep(1000); + while (spuid == null) { + synchronized (spuidMutex) { + try { + spuidMutex.wait(1000); + } catch (InterruptedException e) { - Update(); + } + } + } + + synchronized (events) { + while (events.get() < 1) + try { + events.wait(1000); + } catch (InterruptedException e) { - synchronized (mutex) { - mutex.wait(); + } + } + + Publisher pub = null; + + if (spuid != null) { + pub = new Publisher("VAIMEE", properties, sm, 1); + pub.start(); + + synchronized (events) { + while (events.get() < 2) + try { + events.wait(1000); + } catch (InterruptedException e) { + + } + } } + + sub.finish(); + sub.interrupt(); + + if (pub != null) { + pub.interrupt(); + pub.join(1000); + } + + sub.join(1000); + + assertFalse("Failed to subscribe", spuid == null); + assertFalse("Notification not received " + events.get(), events.get() != 2); } - + @Test(timeout = 5000) - public void NotifyNxM() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, + public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { + int n = 10; + int total = n * n * n; + + ArrayList subscribers = new ArrayList(); + ArrayList publishers = new ArrayList(); - mutex.set(n*n*n); - - for (int i=0; i < n; i++) new Subscriber("ALL",properties,sm,mutex).start(); + Publisher pub; + Subscriber sub; - Thread.sleep(1000); + for (int i = 0; i < n; i++) { + sub = new Subscriber("RANDOM", properties, sm, this); + sub.start(); + subscribers.add(sub); + } - for (int i=0; i < n; i++) new Publisher("RANDOM",properties,sm,n).start(); + while (subscribes.get() < n) { + synchronized (subscribes) { + try { + subscribes.wait(5000); + } catch (InterruptedException e) { - synchronized (mutex) { - while(mutex.get() > 0) mutex.wait(); + } + } } - } - @Test(timeout = 60000) - public void Notify2Nx3M() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, - SEPAPropertiesException, SEPASecurityException { - int n = 10; - int updates = n*n*n; + synchronized (events) { + while (events.get() < n) + try { + events.wait(5000); + } catch (InterruptedException e) { + + } + } + + for (int i = 0; i < n; i++) { + pub = new Publisher("RANDOM", properties, sm, n); + publishers.add(pub); + pub.start(); + } + + for (Publisher pb1 : publishers) + pb1.join(); + + synchronized (events) { + while (events.get() < total+n) + try { + events.wait(5000); + } catch (InterruptedException e) { + + } + } - int nAll = updates * n; - int nRandom = n * n * n; - int nRandom1 = n * n * n; + for (Subscriber sb1 : subscribers) { + sb1.finish(); + sb1.interrupt(); + sb1.join(100); + } - mutex.set(nAll+nRandom+nRandom1); + assertFalse("Failed to subscribe (" + subscribes.get() + " on " + n + ")", subscribes.get() != n); + assertFalse("Received: " + events.get() + " on: " + total+n + " events", events.get() != total+n); + } + + @Test(timeout = 10000) + public void UpdateHeavyLoad() throws InterruptedException { + int n = 10; + + ArrayList pool = new ArrayList(); + Publisher pb; + + for (int i = 0; i < n; i++) { + pb = new Publisher("RANDOM", properties, sm, n); + pool.add(pb); + pb.start(); + pb = new Publisher("RANDOM1", properties, sm, n); + pool.add(pb); + pb.start(); + pb = new Publisher("VAIMEE", properties, sm, n); + pool.add(pb); + pb.start(); + } + + for (Publisher pb1 : pool) + pb1.join(n*500); + } + + @Test(timeout = 10000) + public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, + SEPAPropertiesException, SEPASecurityException { + int n = 10; + + ArrayList subscribers = new ArrayList(); + ArrayList publishers = new ArrayList(); - for (int i=0; i < n; i++) { - new Subscriber("ALL",properties,sm,mutex).start(); - new Subscriber("RANDOM",properties,sm,mutex).start(); - new Subscriber("RANDOM1",properties,sm,mutex).start(); + Publisher pub; + Subscriber sub; + + int total = 4 * n * n * n; + + for (int i = 0; i < n; i++) { + sub = new Subscriber("ALL", properties, sm, this); + sub.start(); + subscribers.add(sub); + + sub =new Subscriber("RANDOM", properties, sm, this); + sub.start(); + subscribers.add(sub); + + sub = new Subscriber("RANDOM1", properties, sm, this); + sub.start(); + subscribers.add(sub); } - Thread.sleep(1000); + while (subscribes.get() < 3 * n) { + synchronized (subscribes) { + try { + subscribes.wait(10000); + } catch (InterruptedException e) { - for (int i=0; i < n; i++) { - new Publisher("RANDOM",properties,sm,n).start(); - new Publisher("RANDOM1",properties,sm,n).start(); + } + } } - synchronized (mutex) { - while(mutex.get() > 0) mutex.wait(); + synchronized (events) { + while (events.get() < 3 * n) + try { + events.wait(5000); + } catch (InterruptedException e) { + + } } + + for (int i = 0; i < n; i++) { + pub = new Publisher("RANDOM", properties, sm, n); + publishers.add(pub); + pub.start(); + + pub = new Publisher("RANDOM1", properties, sm, n); + publishers.add(pub); + pub.start(); + } + + for (Publisher pb1 : publishers) + pb1.join(n*500); + + synchronized (events) { + while (events.get() < total+ 3*n) + try { + events.wait(5000); + } catch (InterruptedException e) { + + } + } + + for (Subscriber sb1 : subscribers) { + sb1.finish(); + sb1.interrupt(); + sb1.join(100); + } + + assertFalse("Failed to subscribe (" + subscribes.get() + " on " + 3 * n + ")", subscribes.get() != 3 * n); + assertFalse("Events not received (" + events.get() + " on " + total + 3*n + ")", events.get() != total+3*n); } - - protected static UpdateRequest buildUpdateRequest(String id, int timeout) throws SEPAPropertiesException, SEPASecurityException { + + protected static UpdateRequest buildUpdateRequest(String id, long timeout) + throws SEPAPropertiesException, SEPASecurityException { HTTPMethod method = properties.getUpdateMethod(id); String scheme = properties.getUpdateProtocolScheme(id); String host = properties.getUpdateHost(id); @@ -212,13 +508,15 @@ protected static UpdateRequest buildUpdateRequest(String id, int timeout) throws String namedGraphUri = properties.getUsingNamedGraphURI(id); String authorization = null; - if (sm != null) authorization = sm.getAuthorizationHeader(); - - return new UpdateRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, - authorization); + if (sm != null) + authorization = sm.getAuthorizationHeader(); + + return new UpdateRequest(method, scheme, host, port, path, sparql, graphUri, namedGraphUri, authorization, + timeout); } - protected static QueryRequest buildQueryRequest(String id, int timeout) throws SEPAPropertiesException, SEPASecurityException { + protected static QueryRequest buildQueryRequest(String id, long timeout) + throws SEPAPropertiesException, SEPASecurityException { HTTPMethod method = properties.getQueryMethod(id); String scheme = properties.getQueryProtocolScheme(id); String host = properties.getQueryHost(id); @@ -229,27 +527,79 @@ protected static QueryRequest buildQueryRequest(String id, int timeout) throws S String namedGraphUri = properties.getNamedGraphURI(id); String authorization = null; - if (sm != null) authorization = sm.getAuthorizationHeader(); - - return new QueryRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, - authorization); + if (sm != null) + authorization = sm.getAuthorizationHeader(); + + return new QueryRequest(method, scheme, host, port, path, sparql, graphUri, namedGraphUri, authorization, + timeout); } - protected static SubscribeRequest buildSubscribeRequest(String id) throws SEPAPropertiesException, SEPASecurityException { + protected static SubscribeRequest buildSubscribeRequest(String id, long timeout) + throws SEPAPropertiesException, SEPASecurityException { String sparql = properties.getSPARQLQuery(id); String graphUri = properties.getDefaultGraphURI(id); String namedGraphUri = properties.getNamedGraphURI(id); - + String authorization = null; - if (sm != null) authorization = sm.getAuthorizationHeader(); + if (sm != null) + authorization = sm.getAuthorizationHeader(); - return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization); + return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization, timeout); } - - private UnsubscribeRequest buildUnsubscribeRequest(String spuid) throws SEPAPropertiesException, SEPASecurityException { + + private UnsubscribeRequest buildUnsubscribeRequest(String spuid, long timeout) + throws SEPAPropertiesException, SEPASecurityException { String authorization = null; - if (sm != null) authorization = sm.getAuthorizationHeader(); - - return new UnsubscribeRequest(spuid, authorization); + if (sm != null) + authorization = sm.getAuthorizationHeader(); + + return new UnsubscribeRequest(spuid, authorization, timeout); + } + + @Override + public void onSemanticEvent(Notification notify) { + logger.trace("@onSemanticEvent " + notify); + synchronized (events) { + events.set(events.get() + 1); + logger.debug("Notifications received: " + events.get()); + events.notify(); + } + } + + @Override + public void onBrokenConnection() { + logger.trace("@onBrokenConnection"); + } + + @Override + public void onError(ErrorResponse errorResponse) { + logger.trace("@onError"); + logger.error(errorResponse.toString()); + } + + @Override + public void onSubscribe(String spuid, String alias) { + logger.trace("@onSubscribe: " + spuid + " alias: " + alias); + + synchronized (subscribes) { + subscribes.set(subscribes.get() + 1); + subscribes.notify(); + } + + synchronized (spuidMutex) { + this.spuid = spuid; + spuidMutex.notify(); + } + + } + + @Override + public void onUnsubscribe(String spuid) { + logger.trace("@onUnsubscribe: " + spuid); + + synchronized (subscribes) { + subscribes.set(subscribes.get() - 1); + subscribes.notify(); + } } } \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java deleted file mode 100644 index f9420bfa..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/MockSubscriptionHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.Response; - -import java.util.concurrent.Semaphore; - -public class MockSubscriptionHandler implements ISubscriptionHandler { - - private Response response; - private Semaphore mutex = new Semaphore(0); - - @Override - public void onSemanticEvent(Notification notify) { - response = notify; - mutex.release(); - } - - @Override - public void onBrokenConnection() { - mutex.release(); - } - - @Override - public void onError(ErrorResponse errorResponse) { - response = errorResponse; - mutex.release(); - } - - public Response getResponse() throws InterruptedException { - mutex.acquire(); - return response; - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java index b65ecf03..24041cb1 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -36,8 +36,9 @@ public void run() { try { client.update(buildUpdateRequest(id,5000)); } catch (SEPAPropertiesException | SEPASecurityException e) { - running.set(running.get()-1); + } + running.set(running.get()-1); } } @@ -59,7 +60,7 @@ protected UpdateRequest buildUpdateRequest(String id, int timeout) throws SEPAPr String authorization = null; if (sm != null) authorization = sm.getAuthorizationHeader(); - return new UpdateRequest(method, scheme, host, port, path, sparql, timeout, graphUri, namedGraphUri, - authorization); + return new UpdateRequest(method, scheme, host, port, path, sparql, graphUri, namedGraphUri, + authorization,timeout); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java index 4e977343..ac4c69b0 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -1,70 +1,51 @@ package it.unibo.arces.wot.sepa.api; -import static org.junit.Assert.assertFalse; - -import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import it.unibo.arces.wot.sepa.api.protocol.websocket.WebSocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.pattern.JSAP; -public class Subscriber extends Thread implements ISubscriptionHandler { - +public class Subscriber extends Thread { + private JSAP properties; private SEPASecurityManager sm; - private AtomicLong mutex; private SPARQL11SEProtocol client = null; - private ISubscriptionProtocol protocol = null; + private SubscriptionProtocol protocol = null; private String id; - + private AtomicBoolean running = new AtomicBoolean(true); - - public Subscriber(String id,JSAP properties, SEPASecurityManager sm,AtomicLong mutex) { + + public Subscriber(String id, JSAP properties, SEPASecurityManager sm, ISubscriptionHandler handler) + throws SEPAProtocolException { this.properties = properties; this.sm = sm; - this.mutex = mutex; this.id = id; - - try { - protocol = new WebSocketSubscriptionProtocol(properties.getDefaultHost(),properties.getSubscribePort(),properties.getSubscribePath()); - } catch (SEPAProtocolException e2) { - assertFalse(e2.getMessage(),true); + + if (sm != null) { + protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), + properties.getSubscribePath(), sm, handler); + client = new SPARQL11SEProtocol(protocol, sm); + } else { + protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), + properties.getSubscribePath(), handler); + client = new SPARQL11SEProtocol(protocol); } - - if (sm != null) - try { - client = new SPARQL11SEProtocol(protocol, this, sm); - } catch (SEPAProtocolException e1) { - assertFalse(e1.getMessage(), true); - } - else - try { - client = new SPARQL11SEProtocol(protocol, this); - } catch (IllegalArgumentException | SEPAProtocolException e1) { - assertFalse(e1.getMessage(), true); - } } - - public void run() { - Response ret = null; + + public void run() { try { - ret = client.subscribe(buildSubscribeRequest(id)); - } catch (SEPAPropertiesException | SEPASecurityException e1) { - assertFalse(e1.getMessage(), true); + client.subscribe(buildSubscribeRequest(id, 5000)); + } catch (SEPAPropertiesException | SEPASecurityException | SEPAProtocolException e1) { + return; } - assertFalse(String.valueOf(ret), ret.isError()); - while(running.get()) { - synchronized(running) { + while (running.get()) { + synchronized (running) { try { running.wait(); } catch (InterruptedException e) { @@ -72,51 +53,27 @@ public void run() { } } } - - try { - client.close(); - } catch (IOException e) { - assertFalse(e.getMessage(), true); - } + + client.close(); } - - public void interrupt() { - super.interrupt(); - - synchronized(running) { - running.set(true); + + public void finish() { + synchronized (running) { + running.set(false); running.notify(); } } - - private SubscribeRequest buildSubscribeRequest(String id) throws SEPAPropertiesException, SEPASecurityException { + + private SubscribeRequest buildSubscribeRequest(String id, long timeout) + throws SEPAPropertiesException, SEPASecurityException { String sparql = properties.getSPARQLQuery(id); String graphUri = properties.getDefaultGraphURI(id); String namedGraphUri = properties.getNamedGraphURI(id); - - String authorization = null; - if (sm != null) authorization = sm.getAuthorizationHeader(); - - return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization); - } - @Override - public void onSemanticEvent(Notification notify) { - synchronized (mutex) { - mutex.set(mutex.get()-1); - mutex.notify(); - } - } - - @Override - public void onBrokenConnection() { - // TODO Auto-generated method stub - - } + String authorization = null; + if (sm != null) + authorization = sm.getAuthorizationHeader(); - @Override - public void onError(ErrorResponse errorResponse) { - // TODO Auto-generated method stub - + return new SubscribeRequest(sparql, id, graphUri, namedGraphUri, authorization, timeout); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java new file mode 100644 index 00000000..c116817b --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java @@ -0,0 +1,22 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import java.net.URL; + +public class ConfigurationProvider { + + public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { + JSAP result; + final String configuaration = System.getProperty("testConfiguration"); + if( configuaration != null){ + result = new JSAP(configuaration); + }else{ + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); + result = new JSAP(config.getPath()); + } + return result; + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java deleted file mode 100644 index 0daf9d63..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java +++ /dev/null @@ -1,118 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import java.net.URI; -import java.net.URISyntaxException; -import java.util.concurrent.atomic.AtomicLong; - -import org.junit.BeforeClass; -import org.junit.Test; - -import it.unibo.arces.wot.sepa.api.MockSubscriptionHandler; -import it.unibo.arces.wot.sepa.api.protocol.websocket.SEPAWebsocketClient; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.Response; - -import static org.junit.Assert.*; - -public class ITSEPAWebsocketClient { - private static int N_THREADS = 100; - private static AtomicLong mutex = new AtomicLong(N_THREADS); - - @BeforeClass - public static void init() throws SEPAPropertiesException, SEPASecurityException { - - } - - @Test(timeout = 10000) - public void Subscribe() { - mutex = new AtomicLong(N_THREADS); - - for (int i = 0; i < N_THREADS; i++) { - new Thread() { - public void run() { - URI ws = null; - try { - ws = new URI("ws://localhost:9000/subscribe"); - } catch (URISyntaxException e) { - assertFalse(e.getMessage(), true); - } - MockSubscriptionHandler handler = new MockSubscriptionHandler(); - - SEPAWebsocketClient client = new SEPAWebsocketClient(ws, handler); - try { - client.connectBlocking(); - } catch (InterruptedException e) { - assertFalse(e.getMessage(), true); - } - if(client.isOpen()) { - client.send("{\"subscribe\":{\"sparql\":\"select * where {?x ?y ?z}\",\"default-graph-uri\":\"http://sepatest\"}}"); - client.close(); - } - - synchronized (mutex) { - mutex.set(mutex.get()-1); - mutex.notify(); - } - } - }.start(); - } - - synchronized (mutex) { - while (mutex.get() > 0) - try { - mutex.wait(); - } catch (InterruptedException e) { - assertFalse(e.getMessage(), true); - } - } - } - - @Test(timeout = 10000) - public void connectSendAndReceive() { - mutex = new AtomicLong(N_THREADS); - - for (int i = 0; i < N_THREADS; i++) { - new Thread() { - public void run() { - URI ws = null; - try { - ws = new URI("ws://localhost:9000/subscribe"); - } catch (URISyntaxException e) { - assertFalse(e.getMessage(), true); - } - MockSubscriptionHandler handler = new MockSubscriptionHandler(); - - SEPAWebsocketClient client = new SEPAWebsocketClient(ws, handler); - try { - client.connectBlocking(); - } catch (InterruptedException e) { - assertFalse(e.getMessage(), true); - } - if(client.isOpen()) { - Response ret = client.sendAndReceive("{\"subscribe\":{\"sparql\":\"select * where {?x ?y ?z}\",\"default-graph-uri\":\"http://sepatest\"}}", 2000); - - assertFalse(ret.toString(),ret.isError()); - - client.close(); - } - - synchronized (mutex) { - mutex.set(mutex.get()-1); - mutex.notify(); - } - } - }.start(); - } - - synchronized (mutex) { - while (mutex.get() > 0) - try { - mutex.wait(); - } catch (InterruptedException e) { - assertFalse(e.getMessage(), true); - } - } - } - -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java new file mode 100644 index 00000000..955761f8 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -0,0 +1,315 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import static org.junit.Assert.*; + +public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { + protected final Logger logger = LogManager.getLogger(); + + private static JSAP app = null; + + private WebsocketSubscriptionProtocol client = null; + private static SEPASecurityManager sm = null; + + private static AtomicLong events = new AtomicLong(0); + private static AtomicLong subscribes = new AtomicLong(0); + private static AtomicLong brokens = new AtomicLong(0); + + private String spuid = null; + private static final Object spuidMutex = new Object(); + + @BeforeClass + public static void init() throws SEPAPropertiesException, SEPASecurityException { + app = ConfigurationProvider.GetTestEnvConfiguration(); + if (app.isSecure()) + sm = new SEPASecurityManager(app.getAuthenticationProperties()); + } + + @Before + public void before() + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, URISyntaxException { + if (app.isSecure()) { + sm.register("SEPATest"); + client = new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), + app.getSubscribePath(), sm, this); + } else + client = new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), + app.getSubscribePath(), this); + + events.set(0); + subscribes.set(0); + brokens.set(0); + } + + @Test(timeout = 5000) + public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + SubscribeRequest request; + if (app.isSecure()) { + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); + } else + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), null, 5000); + + logger.debug(request); + + client.subscribe(request); + + while(subscribes.get() != 1) { + synchronized(subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + } + + client.close(); + + assertFalse("Failed to subscribe",subscribes.get() != 1); + } + + @Test(timeout = 20000) + public void BrokenSockets() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + int n = 5; + + SubscribeRequest request; + ArrayList threadPoll = new ArrayList(); + Thread th; + + for (int i = 0; i < n; i++) { + if (app.isSecure()) { + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); + } else + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), null, 5000); + th = new Thread(new WebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, + request,this)); + threadPoll.add(th); + th.start(); + + if (app.isSecure()) { + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); + } else + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), null, 5000); + th = new Thread(new WebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, + request,this)); + threadPoll.add(th); + th.start(); + + if (app.isSecure()) { + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", + app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), sm.getAuthorizationHeader(), + 5000); + } else + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", + app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), null, 5000); + th = new Thread(new WebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, + request,this)); + threadPoll.add(th); + th.start(); + } + + while(subscribes.get() != n * 3) { + synchronized(subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to subscribe",subscribes.get() != n * 3); + + for (Thread th1 : threadPoll) { + try { + th1.join(); + } catch (InterruptedException e) { + + } + } + + while(events.get() != n * 3) { + synchronized(events) { + try { + events.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to receive all first notifications",events.get() != n * 3); + + while(brokens.get() != n * 3) { + synchronized(brokens) { + try { + brokens.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to receive all broken notifications",brokens.get() != n * 3); + } + + @Test(timeout = 5000) + public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + int n = 100; + + SubscribeRequest request; + if (app.isSecure()) { + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 500); + } else + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), null, 500); + + for (int i = 0; i < n; i++) { + logger.debug(request); + client.subscribe(request); + } + + while(subscribes.get() < n) { + synchronized(subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to subscribe",subscribes.get() != n); + + client.close(); + } + + @Test(timeout = 5000) + public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + SubscribeRequest request; + if (app.isSecure()) { + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); + } else + request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), + app.getNamedGraphURI("ALL"), null, 5000); + + spuid = null; + client.subscribe(request); + + while(subscribes.get() != 1) { + synchronized(subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to subscribe",subscribes.get() != 1); + + UnsubscribeRequest unsub; + if (app.isSecure()) { + unsub = new UnsubscribeRequest(spuid, sm.getAuthorizationHeader(), 5000); + } else { + unsub = new UnsubscribeRequest(spuid, null, 5000); + } + + client.unsubscribe(unsub); + + while(subscribes.get() != 0) { + synchronized(subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to unsubscribe",subscribes.get() != 0); + + client.close(); + } + + @Override + public void onSemanticEvent(Notification notify) { + logger.debug("@onSemanticEvent: "+notify); + + synchronized(events) { + events.set(events.get()+1); + events.notify(); + } + + logger.debug("Number of events: "+events.get()); + } + + @Override + public void onBrokenConnection() { + logger.debug("@onBrokenConnection"); + + synchronized(brokens) { + brokens.set(brokens.get()+1); + brokens.notify(); + } + } + + @Override + public void onError(ErrorResponse errorResponse) { + logger.error("@onError: "+errorResponse); + } + + @Override + public void onSubscribe(String spuid, String alias) { + logger.debug("@onSubscribe: "+spuid+" alias: "+alias); + + synchronized(subscribes) { + subscribes.set(subscribes.get()+1); + subscribes.notify(); + } + + synchronized (spuidMutex) { + this.spuid = spuid; + spuidMutex.notify(); + } + } + + @Override + public void onUnsubscribe(String spuid) { + logger.debug("@onUnsubscribe "+spuid); + synchronized(subscribes) { + subscribes.set(subscribes.get()-1); + subscribes.notify(); + } + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java new file mode 100644 index 00000000..805c597e --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java @@ -0,0 +1,39 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import static org.junit.Assert.assertFalse; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +public class WebsocketClient implements Runnable { + private WebsocketSubscriptionProtocol client = null; + private SubscribeRequest request = null; + + public WebsocketClient(String host,int port,String path,SEPASecurityManager sm,SubscribeRequest request,ISubscriptionHandler handler) throws SEPAProtocolException { + if (sm != null) client = new WebsocketSubscriptionProtocol(host, port, + path, sm, handler); + else client = new WebsocketSubscriptionProtocol(host, port, + path, handler); + this.request = request; + } + + + public void run() { + try { + client.subscribe(request); + } catch (SEPAProtocolException e1) { + assertFalse(e1.getMessage(),true); + } + + try { + Thread.sleep((long) (500+500*Math.random())); + } catch (InterruptedException e) { + + } + + client.close(); + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java new file mode 100644 index 00000000..40676e08 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java @@ -0,0 +1,22 @@ +package it.unibo.arces.wot.sepa.commons.security; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import java.net.URL; + +public class ConfigurationProvider { + + public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { + JSAP result; + final String configuaration = System.getProperty("testConfiguration"); + if( configuaration != null){ + result = new JSAP(configuaration); + }else{ + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); + result = new JSAP(config.getPath()); + } + return result; + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java new file mode 100644 index 00000000..0fe401bf --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -0,0 +1,48 @@ +package it.unibo.arces.wot.sepa.commons.security; + +import org.junit.BeforeClass; +import org.junit.Test; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import static org.junit.Assert.*; + +public class ITSEPASecurityManager { + private static SEPASecurityManager sm = null; + + private String testId = "SEPATest"; + private String notAllowedId = "IamNotAllowedToRegister"; + + @BeforeClass + public static void init() throws SEPAPropertiesException, SEPASecurityException { + JSAP app = ConfigurationProvider.GetTestEnvConfiguration(); + if (app.isSecure()) sm = new SEPASecurityManager(app.getAuthenticationProperties()); + } + + @Test(timeout = 2000) + public void Register() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + Response ret = sm.register(testId); + assertFalse(String.valueOf(ret),ret.isError()); + + ret = sm.register(notAllowedId); + assertFalse(String.valueOf(ret),!ret.isError()); + } + + @Test(timeout = 15000) + public void GetAuthorizationHeader() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { + Response ret = sm.register(testId); + assertFalse(String.valueOf(ret),ret.isError()); + + // In test conditions token expires in 5 seconds + for (int i=0; i < 10; i++) { + sm.getAuthorizationHeader(); + Thread.sleep(1000); + } + } + +} diff --git a/client-api/src/test/resources/log4j2.xml b/client-api/src/test/resources/log4j2.xml new file mode 100644 index 00000000..be72e020 --- /dev/null +++ b/client-api/src/test/resources/log4j2.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index 46cb2a66..f140bee2 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -46,19 +46,31 @@ "DELETE_ALL" : { "sparql" : "delete where {?x ?y ?z}" }, - "U1": { + "VAIMEE": { "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" }, - "U2": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P 123} where {OPTIONAL{?s ?p ?o}}" + "RANDOM": { + "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + }, + "RANDOM1": { + "sparql": "delete {?s ?p ?o} insert {sepa:S1 sepa:P1 ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" } }, "queries": { - "Q1": { + "VAIMEE": { "sparql": "select * where {?x ?y \"ვაიმეე\"}" }, - "Q2": { + "ALL": { "sparql": "select * where {?x ?y ?z}" + }, + "RANDOM": { + "sparql": "select * where {sepa:S sepa:P ?random}" + }, + "RANDOM1": { + "sparql": "select * where {sepa:S1 sepa:P1 ?random}" + }, + "COUNT": { + "sparql": "select (COUNT(?x) AS ?n) where {?x ?y ?z}" } } } diff --git a/client-api/src/test/resources/sepatest.jsap b/client-api/src/test/resources/sepatest.jsap index 92886be0..20a5cd76 100644 --- a/client-api/src/test/resources/sepatest.jsap +++ b/client-api/src/test/resources/sepatest.jsap @@ -47,13 +47,13 @@ "sparql" : "delete where {?x ?y ?z}" }, "VAIMEE": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" + "sparql": "delete {sepa:SV sepa:PV ?o} where {sepa:SV sepa:PV ?o} ; insert data {sepa:SV sepa:PV \"ვაიმეე\"}" }, "RANDOM": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "delete {sepa:S sepa:P ?o} where {sepa:S sepa:P ?o} ; insert {sepa:S sepa:P ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" }, "RANDOM1": { - "sparql": "delete {?s ?p ?o} insert {sepa:S1 sepa:P1 ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "delete {sepa:S1 sepa:P1 ?o} where {sepa:S1 sepa:P1 ?o} ; insert {sepa:S1 sepa:P1 ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" } }, "queries": { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java index afa806ed..5cf476ba 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/HTTPHandlerBeans.java @@ -19,6 +19,8 @@ public class HTTPHandlerBeans { private long requestHandlingMaxTime = -1; private long handledRequests = 0; + private int outOfTokens; + private HashMap timings = new HashMap(); public void reset() { @@ -33,6 +35,8 @@ public void reset() { requestHandlingMinTime = -1; requestHandlingMaxTime = -1; handledRequests = 0; + + outOfTokens = 0; } public synchronized long start(HttpAsyncExchange handler) { @@ -148,4 +152,12 @@ public void validatingFailed() { public void authorizingFailed() { authorizingFailedRequests++; } + + public void outOfTokens() { + outOfTokens++; + } + + public long getErrors_OutOfTokens() { + return outOfTokens; + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java index 84a99a7e..8ef4d799 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java @@ -1,10 +1,6 @@ package it.unibo.arces.wot.sepa.engine.bean; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; public class SchedulerBeans { @@ -63,16 +59,16 @@ public static void tokenLeft(int size) { } - public static void newRequest(Request req,boolean scheduled) { + public static void newRequest(InternalRequest req,boolean scheduled) { if (scheduled) scheduledRequests++; else outOfTokenRequests++; - if (req.getClass().equals(UpdateRequest.class)) totalUpdateRequests++; - else if (req.getClass().equals(QueryRequest.class)) totalQueryRequests++; - else if (req.getClass().equals(SubscribeRequest.class)) totalSubscribeRequests++; - else if (req.getClass().equals(UnsubscribeRequest.class)) totalUnsubscribeRequests++; + if (req.isUpdateRequest()) totalUpdateRequests++; + else if (req.isQueryRequest()) totalQueryRequests++; + else if (req.isSubscribeRequest()) totalSubscribeRequests++; + else if (req.isUnsubscribeRequest()) totalUnsubscribeRequests++; } public static long getScheduledRequests() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java index f324c70e..353f2280 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java @@ -3,25 +3,25 @@ import it.unibo.arces.wot.sepa.timing.Timings; public class WebsocketBeans { - private long messages = 0; - private long notAuthorized = 0; - private long errors = 0; - private long fragments = 0; + private static long messages = 0; + private static long notAuthorized = 0; + private static long errors = 0; + private static long fragments = 0; - private long subscribeHandlingTime = -1; - private float subscribeHandlingAverageTime = -1; - private long subscribeHandlingMinTime = -1; - private long subscribeHandlingMaxTime = -1; - private long handledSubscribes = 0; + private static long subscribeHandlingTime = -1; + private static float subscribeHandlingAverageTime = -1; + private static long subscribeHandlingMinTime = -1; + private static long subscribeHandlingMaxTime = -1; + private static long handledSubscribes = 0; - private long unsubscribeHandlingTime = -1; - private float unsubscribeHandlingAverageTime = -1; - private long unsubscribeHandlingMinTime = -1; - private long unsubscribeHandlingMaxTime = -1; - private long handledunsubscribes = 0; + private static long unsubscribeHandlingTime = -1; + private static float unsubscribeHandlingAverageTime = -1; + private static long unsubscribeHandlingMinTime = -1; + private static long unsubscribeHandlingMaxTime = -1; + private static long handledunsubscribes = 0; - public long unsubscribeTimings(long start) { + public static long unsubscribeTimings(long start) { handledunsubscribes++; unsubscribeHandlingTime = Timings.getTime() - start; @@ -69,42 +69,54 @@ else if (subscribeHandlingTime > subscribeHandlingMaxTime) return subscribeHandlingTime; } - public void reset() { + public static void reset() { fragments = 0; messages = 0; errors = 0; notAuthorized = 0; + + subscribeHandlingTime = -1; + subscribeHandlingAverageTime = -1; + subscribeHandlingMinTime = -1; + subscribeHandlingMaxTime = -1; + handledSubscribes = 0; + + unsubscribeHandlingTime = -1; + unsubscribeHandlingAverageTime = -1; + unsubscribeHandlingMinTime = -1; + unsubscribeHandlingMaxTime = -1; + handledunsubscribes = 0; } - public long getMessages(){ + public static long getMessages(){ return messages; } - public long getFragmented(){ + public static long getFragmented(){ return fragments; } - public long getErrors(){ + public static long getErrors(){ return errors; } - public long getNotAuthorized(){ + public static long getNotAuthorized(){ return notAuthorized; } - public void onError() { + public static void onError() { errors++; } - public void onFragmentedMessage() { + public static void onFragmentedMessage() { fragments++; } - public void onNotAuthorizedRequest() { + public static void onNotAuthorizedRequest() { notAuthorized++; } - public void onMessage() { + public static void onMessage() { messages++; } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 98b31206..18efa622 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -28,8 +28,6 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.PatternSyntaxException; import org.apache.logging.log4j.LogManager; @@ -45,14 +43,15 @@ import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; -import it.unibo.arces.wot.sepa.engine.processing.ProcessorThread; + +import it.unibo.arces.wot.sepa.engine.processing.Processor; + import it.unibo.arces.wot.sepa.engine.protocol.websocket.WebsocketServer; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpGate; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpsGate; import it.unibo.arces.wot.sepa.engine.protocol.websocket.SecureWebsocketServer; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; -import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; /** * This class represents the SPARQL Subscription Broker (Core) of the SPARQL @@ -68,16 +67,13 @@ public class Engine implements EngineMBean { private EngineProperties properties = null; // Scheduler request queue - private final SchedulerRequestResponseQueue schedulerQueue = new SchedulerRequestResponseQueue(); - - // Broken spuids queue - private final BlockingQueue killSpuids = new LinkedBlockingQueue(); + //private final SchedulerQueue schedulerQueue = new SchedulerQueue(); // Primitives scheduler/dispatcher private Scheduler scheduler = null; // Primitives scheduler/dispatcher - private ProcessorThread processor = null; + private Processor processor = null; // SPARQL 1.1 Protocol handler private HttpGate httpGate = null; @@ -241,20 +237,19 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException } // SPARQL 1.1 SE request scheduler - scheduler = new Scheduler(properties, schedulerQueue); + scheduler = new Scheduler(properties); scheduler.start(); // Dependability manager - dependabilityMng = new DependabilityManager(killSpuids); + dependabilityMng = new DependabilityManager(scheduler.getSchedulerQueue()); // SEPA Processor try { - processor = new ProcessorThread(endpointProperties, properties, schedulerQueue,killSpuids); + processor = new Processor(endpointProperties, properties, scheduler.getSchedulerQueue()); } catch (SEPAProtocolException e1) { System.err.println(e1.getMessage()); System.exit(1); } - processor.setName("SEPA-Processor"); processor.start(); // SPARQL protocol service diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java index a3723a3d..b8510c8d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EventHandler.java @@ -1,9 +1,8 @@ package it.unibo.arces.wot.sepa.engine.core; -import java.io.IOException; - +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.response.Notification; -public interface EventHandler extends ResponseHandler { - public void notifyEvent(Notification notify) throws IOException; +public interface EventHandler { + public void notifyEvent(Notification notify) throws SEPAProtocolException; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java index 4cb94dc0..4d6e048f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java @@ -1,9 +1,8 @@ package it.unibo.arces.wot.sepa.engine.core; -import java.io.IOException; - +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.response.Response; public interface ResponseHandler { - public void sendResponse(Response response) throws IOException; + public void sendResponse(Response response) throws SEPAProtocolException; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java index 5bc0d3b8..8baea394 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java @@ -369,17 +369,17 @@ public Response getToken(String encodedCredentials) { } catch (IllegalArgumentException e) { logger.error("Not authorized"); - return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); } String decodedCredentials = new String(decoded); String[] clientID = decodedCredentials.split(":"); if (clientID==null){ logger.error("Wrong Basic authorization"); - return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); } if (clientID.length != 2) { logger.error("Wrong Basic authorization"); - return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); } String id = decodedCredentials.split(":")[0]; @@ -389,12 +389,12 @@ public Response getToken(String encodedCredentials) { //Verify credentials if (!credentials.containsKey(id)) { logger.error("Client id: "+id+" is not registered"); - return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); } if (!credentials.get(id).equals(secret)) { logger.error("Wrong secret: "+secret+ " for client id: "+id); - return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); } //Check is a token has been release for this client @@ -405,7 +405,7 @@ public Response getToken(String encodedCredentials) { logger.debug("Check token expiration: "+now+" > "+expires+ " ?"); if(now.before(expires)) { logger.warn("Token is not expired"); - return new ErrorResponse(0,HttpStatus.SC_BAD_REQUEST,"Token is not expired"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"Token is not expired"); } } @@ -510,13 +510,13 @@ public Response getToken(String encodedCredentials) { signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), JWTClaimsSet.parse(jwtClaims.toString())); } catch (ParseException e) { logger.error(e.getMessage()); - return new ErrorResponse(0,HttpStatus.SC_INTERNAL_SERVER_ERROR,"Error on signing JWT (1)"); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"Error on signing JWT (1)"); } try { signedJWT.sign(signer); } catch (JOSEException e) { logger.error(e.getMessage()); - return new ErrorResponse(0,HttpStatus.SC_INTERNAL_SERVER_ERROR,"Error on signing JWT (2)"); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"Error on signing JWT (2)"); } //Add the token to the released tokens @@ -537,7 +537,7 @@ public Response validateToken(String accessToken) { } try { - if(!signedJWT.verify(verifier)) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED); + if(!signedJWT.verify(verifier)) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Signed JWT not verified"); } catch (JOSEException e) { return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,e.getMessage()); @@ -553,9 +553,9 @@ public Response validateToken(String accessToken) { //Check token expiration Date now = new Date(); - if (now.after(claimsSet.getExpirationTime())) return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Token is expired "+claimsSet.getExpirationTime()); + if (now.after(claimsSet.getExpirationTime())) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Token is expired "+claimsSet.getExpirationTime()); - if (now.before(claimsSet.getNotBeforeTime())) return new ErrorResponse(0,HttpStatus.SC_UNAUTHORIZED,"Token can not be used before: "+claimsSet.getNotBeforeTime()); + if (now.before(claimsSet.getNotBeforeTime())) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Token can not be used before: "+claimsSet.getNotBeforeTime()); return new JWTResponse(accessToken,"bearer",now.getTime()-claimsSet.getExpirationTime().getTime()); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java index 2ab8fbee..35312a20 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java @@ -2,77 +2,72 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.concurrent.BlockingQueue; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.java_websocket.WebSocket; + +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerQueue; public class DependabilityManager { private static final Logger logger = LogManager.getLogger(); - // Active sockets - private HashMap> subscriptions = new HashMap>(); + // Active subscriptions + private static final HashMap> subscriptions = new HashMap>(); - // Broken sockets - private ArrayList brokenSockets = new ArrayList(); + // Broken subscriptions + private static final ArrayList brokenBeforeSubscribe = new ArrayList(); - private final BlockingQueue killSpuids; + // Scheduler queue + private final SchedulerQueue schedulerQueue; - public DependabilityManager(BlockingQueue spuids) { - this.killSpuids = spuids; + public DependabilityManager(SchedulerQueue schedulerQueue) { + this.schedulerQueue = schedulerQueue; } - public void onSubscribe(WebSocket conn, String spuid) { - logger.debug("@onSubscribe: "+conn+" SPUID: "+spuid); - - if (brokenSockets.contains(conn)) { - logger.debug("Socket has been closed: "+conn+" SPUID: "+spuid); - logger.debug("Request to kill SPU: "+spuid); - try { - killSpuids.put(spuid); - } catch (InterruptedException e) { - logger.warn(e.getMessage()); - } + public synchronized void onSubscribe(Integer hash, String spuid) { + logger.debug("@onSubscribe: "+hash+" SPUID: "+spuid); + + if (brokenBeforeSubscribe.contains(hash)) { + logger.debug("Subscription has already been closed: " + hash + " Schedule to kill SPUID: " + spuid); + schedulerQueue.killSpuid(spuid); return; } - if (!subscriptions.containsKey(conn)) - subscriptions.put(conn, new ArrayList()); - subscriptions.get(conn).add(spuid); + if (!subscriptions.containsKey(hash)) subscriptions.put(hash, new ArrayList()); + + subscriptions.get(hash).add(spuid); } - public void onUnsubscribe(WebSocket conn, String spuid) { - logger.debug("@onUnsubscribe: "+conn+" SPUID: "+spuid); + public synchronized void onUnsubscribe(Integer hash, String spuid) { + logger.debug("@onUnsubscribe: "+hash+" SPUID: "+spuid); - if (subscriptions.containsKey(conn)) { - subscriptions.get(conn).remove(spuid); - if (subscriptions.get(conn).isEmpty()) - subscriptions.remove(conn); - } + subscriptions.get(hash).remove(spuid); + if (subscriptions.get(hash).isEmpty()) subscriptions.remove(hash); } - public void onBrokenSocket(WebSocket conn) { - logger.debug("@onBrokenSocket: "+conn); + public synchronized void onBrokenSocket(Integer hash) { + logger.debug("@onBrokenSocket: "+hash); - if (!subscriptions.containsKey(conn)) { - brokenSockets.add(conn); + if (!subscriptions.containsKey(hash)) { + logger.debug("Broken before subscribe: "+hash); + brokenBeforeSubscribe.add(hash); return; } - logger.debug(String.format("Broken socket with %d active subscriptions", subscriptions.get(conn).size())); + logger.debug(String.format("Broken socket with active subscriptions: %d", subscriptions.get(hash).size())); // Kill all SPUs - for (String spuid : subscriptions.get(conn)) { - logger.debug("Request to kill SPU: "+spuid); - try { - killSpuids.put(spuid); - } catch (InterruptedException e) { - logger.warn(e.getMessage()); - } + for (String spuid : subscriptions.get(hash)) { + logger.debug("Schedule request to kill SPU: "+spuid); + schedulerQueue.killSpuid(spuid); } // Remove subscriptions - subscriptions.remove(conn); + subscriptions.remove(hash); + } + + public synchronized void onError(Integer hash, ErrorResponse error) { + logger.error("Subscription:"+hash + " error:"+error); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java new file mode 100644 index 00000000..af99067a --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -0,0 +1,165 @@ +/* This class implements the processing of the requests coming form the scheduler + * + * Author: Luca Roffia (luca.roffia@unibo.it) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package it.unibo.arces.wot.sepa.engine.processing; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicBoolean; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; +import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.QueryProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; +import it.unibo.arces.wot.sepa.engine.bean.UpdateProcessorBeans; +import it.unibo.arces.wot.sepa.engine.core.EngineProperties; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; +import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerQueue; + +public class Processor implements ProcessorMBean { + // Processor threads + private final UpdateProcessingThread updateProcessingThread; + private final SubscribeProcessingThread subscribeProcessingThread; + private final QueryProcessingThread queryProcessingThread; + + // SPARQL Processors + private final QueryProcessor queryProcessor; + private final UpdateProcessor updateProcessor; + + // SPU manager + private final SPUManager spuManager; + + // Concurrent endpoint limit + private Semaphore endpointSemaphore = null; + + // Scheduler queue + private final SchedulerQueue queue; + + // Running flag + private final AtomicBoolean running = new AtomicBoolean(true); + + public Processor(SPARQL11Properties endpointProperties, EngineProperties properties, + SchedulerQueue queue) throws IllegalArgumentException, SEPAProtocolException { + + // Number of maximum concurrent requests (supported by the endpoint) + int max = properties.getMaxConcurrentRequests(); + // TODO: extending at run-time the semaphore max + if (max > 0) endpointSemaphore = new Semaphore(max, true); + + this.queue = queue; + + // Processors + queryProcessor = new QueryProcessor(endpointProperties,endpointSemaphore); + updateProcessor = new UpdateProcessor(endpointProperties,endpointSemaphore); + + // SPU Manager + spuManager = new SPUManager(this); + + // Subscribe/Unsubscribe processing + subscribeProcessingThread = new SubscribeProcessingThread(this); + + // Update processor + updateProcessingThread = new UpdateProcessingThread(this); + + // Query processing + queryProcessingThread = new QueryProcessingThread(this); + + // JMX + SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); + + ProcessorBeans.setEndpoint(endpointProperties, max); + + QueryProcessorBeans.setTimeout(properties.getQueryTimeout()); + + UpdateProcessorBeans.setTimeout(properties.getUpdateTimeout()); + UpdateProcessorBeans.setReilable(properties.isUpdateReliable()); + } + + public boolean isRunning() { + return running.get(); + } + + public SchedulerQueue getSchedulerQueue() { + return queue; + } + + public SPUManager getSPUManager() { + return spuManager; + } + + public QueryProcessor getQueryProcessor() { + return queryProcessor; + } + + public UpdateProcessor getUpdateProcessor() { + return updateProcessor; + } + + public void start() { + running.set(true); + queryProcessingThread.start(); + subscribeProcessingThread.start(); + updateProcessingThread.start(); + } + + public void interrupt() { + running.set(false); + queryProcessingThread.interrupt(); + subscribeProcessingThread.interrupt(); + updateProcessingThread.interrupt(); + } + + public boolean isUpdateReilable() { + return UpdateProcessorBeans.getReilable(); + } + + @Override + public String getEndpointHost() { + return ProcessorBeans.getEndpointHost(); + } + + @Override + public int getEndpointPort() { + return ProcessorBeans.getEndpointPort(); + } + + @Override + public String getEndpointQueryPath() { + return ProcessorBeans.getEndpointQueryPath(); + } + + @Override + public String getEndpointUpdatePath() { + return ProcessorBeans.getEndpointUpdatePath(); + } + + @Override + public String getEndpointUpdateMethod() { + return ProcessorBeans.getEndpointUpdateMethod(); + } + + @Override + public String getEndpointQueryMethod() { + return ProcessorBeans.getEndpointQueryMethod(); + } + + @Override + public int getMaxConcurrentRequests() { + return ProcessorBeans.getMaxConcurrentRequests(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java similarity index 89% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java index 2dbe18d1..602d239f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThreadMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorMBean.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.processing; -public interface ProcessorThreadMBean { +public interface ProcessorMBean { public String getEndpointHost(); public int getEndpointPort(); public String getEndpointQueryPath(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java deleted file mode 100644 index ae6310da..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/ProcessorThread.java +++ /dev/null @@ -1,214 +0,0 @@ -/* This class implements the processing of the requests coming form the scheduler - * - * Author: Luca Roffia (luca.roffia@unibo.it) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.engine.processing; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Semaphore; - -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; -import it.unibo.arces.wot.sepa.engine.bean.QueryProcessorBeans; -import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -import it.unibo.arces.wot.sepa.engine.bean.UpdateProcessorBeans; -import it.unibo.arces.wot.sepa.engine.core.EngineProperties; -import it.unibo.arces.wot.sepa.engine.core.EventHandler; -import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; - -import org.apache.logging.log4j.LogManager; - -public class ProcessorThread extends Thread implements ProcessorThreadMBean { - private final Logger logger = LogManager.getLogger(); - - // Processors - private final UpdateProcessingThread updateProcessor; - private final QueryProcessor queryProcessor; - private final SubscribeProcessor subscribeProcessor; - - // Broken SPU killer - private final SpuKillerThread spuKiller; - - // Scheduler queue - private SchedulerRequestResponseQueue schedulerQueue; - - // Concurrent endpoint limit - private Semaphore endpointSemaphore = null; - - public ProcessorThread(SPARQL11Properties endpointProperties, EngineProperties properties, - SchedulerRequestResponseQueue queue,BlockingQueue killSpuids) throws IllegalArgumentException, SEPAProtocolException { - if (queue == null) { - logger.error("Queue is null"); - throw new IllegalArgumentException("Queue is null"); - } - this.schedulerQueue = queue; - - // TODO: extending at run-time the semaphore max - // Number of maximum concurrent requests (supported by the endpoint) - int max = properties.getMaxConcurrentRequests(); - if (max > 0) - endpointSemaphore = new Semaphore(max, true); - - // Query processor - queryProcessor = new QueryProcessor(endpointProperties, endpointSemaphore); - - // SPU manager - subscribeProcessor = new SubscribeProcessor(endpointProperties, properties, endpointSemaphore); - - // Update processor - if (properties.isUpdateReliable()) - updateProcessor = new UpdateProcessingThread(new UpdateProcessor(endpointProperties, endpointSemaphore), - subscribeProcessor, queue); - else - updateProcessor = new UpdateProcessingThread(new UpdateProcessor(endpointProperties, endpointSemaphore), - subscribeProcessor, null); - - // SPU killer - spuKiller = new SpuKillerThread(killSpuids,subscribeProcessor); - - // JMX - SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); - ProcessorBeans.setEndpoint(endpointProperties, max); - QueryProcessorBeans.setTimeout(properties.getQueryTimeout()); - UpdateProcessorBeans.setTimeout(properties.getUpdateTimeout()); - UpdateProcessorBeans.setReilable(properties.isUpdateReliable()); - } - - @Override - public void run() { - while (true) { - // WAIT NEW REQUEST - ScheduledRequest scheduledRequest; - try { - scheduledRequest = schedulerQueue.waitRequest(); - } catch (InterruptedException e1) { - return; - } - - Request request = scheduledRequest.getRequest(); - if (request.isUpdateRequest()) { - logger.debug("Update request #" + request.getToken()); - logger.trace(request); - - // Update response QoS - if (UpdateProcessorBeans.getReilable()) { - updateProcessor.setSchedulerQueue(schedulerQueue); - } - else { - updateProcessor.setSchedulerQueue(null); - schedulerQueue.addResponse(new UpdateResponse(request.getToken(),"{\"Request scheduled for processing\"}")); - } - - // Add a new UpdateRequest to be processed - updateProcessor.process((UpdateRequest) request); - } else if (request.isQueryRequest()) { - logger.debug("Query request #" + request.getToken()); - logger.trace(request); - - Thread queryProcessing = new Thread() { - public void run() { - Response ret = queryProcessor.process((QueryRequest) request); - schedulerQueue.addResponse(ret); - } - }; - queryProcessing.setName("SEPA-Query-Processing-Thread-" + request.getToken()); - queryProcessing.start(); - } else if (request.isSubscribeRequest()) { - logger.debug("Subscribe request #" + request.getToken()); - logger.trace(request); - - Response ret = subscribeProcessor.subscribe((SubscribeRequest) request, - (EventHandler) scheduledRequest.getHandler()); - - schedulerQueue.addResponse(ret); - } else if (request.isUnsubscribeRequest()) { - logger.info("Unsubscribe request #" + request.getToken()); - logger.debug(request); - - Response ret = subscribeProcessor.unsubscribe((UnsubscribeRequest) request); - - schedulerQueue.addResponse(ret); - } - } - } - - @Override - public synchronized void start() { - super.start(); - subscribeProcessor.start(); - updateProcessor.start(); - spuKiller.start(); - } - - @Override - public void interrupt() { - super.interrupt(); - subscribeProcessor.stop(); - - updateProcessor.finish(); - updateProcessor.interrupt(); - - spuKiller.finish(); - spuKiller.interrupt(); - } - - @Override - public String getEndpointHost() { - return ProcessorBeans.getEndpointHost(); - } - - @Override - public int getEndpointPort() { - return ProcessorBeans.getEndpointPort(); - } - - @Override - public String getEndpointQueryPath() { - return ProcessorBeans.getEndpointQueryPath(); - } - - @Override - public String getEndpointUpdatePath() { - return ProcessorBeans.getEndpointUpdatePath(); - } - - @Override - public String getEndpointUpdateMethod() { - return ProcessorBeans.getEndpointUpdateMethod(); - } - - @Override - public String getEndpointQueryMethod() { - return ProcessorBeans.getEndpointQueryMethod(); - } - - @Override - public int getMaxConcurrentRequests() { - return ProcessorBeans.getMaxConcurrentRequests(); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java new file mode 100644 index 00000000..d80a7c05 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java @@ -0,0 +1,30 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalQueryRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; + +class QueryProcessingThread extends Thread{ + private final Processor processor; + + public QueryProcessingThread(Processor processor) { + this.processor = processor; + setName("SEPA-Query-Processing"); + } + + public void run() { + while(processor.isRunning()) { + ScheduledRequest request; + try { + request = processor.getSchedulerQueue().waitQueryRequest(); + } catch (InterruptedException e) { + return; + } + + InternalQueryRequest query = (InternalQueryRequest) request.getRequest(); + Response ret = processor.getQueryProcessor().process(query); + + processor.getSchedulerQueue().addResponse(request.getToken(),ret); + } + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index 036a07ac..8a9e5426 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -34,14 +34,15 @@ import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.engine.bean.QueryProcessorBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalQueryRequest; import it.unibo.arces.wot.sepa.timing.Timings; public class QueryProcessor implements QueryProcessorMBean { private static final Logger logger = LogManager.getLogger(); - private SPARQL11Protocol endpoint; - private Semaphore endpointSemaphore; - private SPARQL11Properties properties; + private final SPARQL11Protocol endpoint; + private final Semaphore endpointSemaphore; + private final SPARQL11Properties properties; public QueryProcessor(SPARQL11Properties properties, Semaphore endpointSemaphore) throws SEPAProtocolException { this.endpoint = new SPARQL11Protocol(); @@ -51,7 +52,7 @@ public QueryProcessor(SPARQL11Properties properties, Semaphore endpointSemaphore SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); } - public synchronized Response process(QueryRequest req) { + public Response process(InternalQueryRequest req) { long start = Timings.getTime(); if (endpointSemaphore != null) @@ -74,10 +75,10 @@ public synchronized Response process(QueryRequest req) { Response ret; QueryRequest request; - request = new QueryRequest(req.getToken(), properties.getQueryMethod(), properties.getDefaultProtocolScheme(), + request = new QueryRequest(properties.getQueryMethod(), properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), properties.getDefaultQueryPath(), - req.getSPARQL(), QueryProcessorBeans.getTimeout(), req.getDefaultGraphUri(), req.getNamedGraphUri(), - authorizationHeader); + req.getSparql(), req.getDefaultGraphUri(), req.getNamedGraphUri(), + authorizationHeader,QueryProcessorBeans.getTimeout()); ret = endpoint.query(request); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java deleted file mode 100644 index 47b953a0..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SpuKillerThread.java +++ /dev/null @@ -1,35 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; - -public class SpuKillerThread extends Thread { - private final AtomicBoolean end = new AtomicBoolean(false); - - private BlockingQueue queue; - private SubscribeProcessor processor; - - public SpuKillerThread(BlockingQueue killSpuids,SubscribeProcessor processor) { - this.queue = killSpuids; - this.processor = processor; - } - - public void run() { - while (!end.get()) { - String spuid; - try { - spuid = queue.take(); - } catch (InterruptedException e) { - return; - } - - processor.unsubscribe(new UnsubscribeRequest(spuid)); - } - } - - public void finish(){ - end.set(true); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java new file mode 100644 index 00000000..9f827a71 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java @@ -0,0 +1,293 @@ +package it.unibo.arces.wot.sepa.engine.processing; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; +import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; +import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; +import it.unibo.arces.wot.sepa.engine.core.EventHandler; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPU; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; + +class SubscribeProcessingThread extends Thread implements SubscribeProcessingThreadMBean, EventHandler { + private static final Logger logger = LogManager.getLogger(); + + private final Processor processor; + + // Maps + private final HashMap> activeSpus = new HashMap>(); + private final HashMap spuids = new HashMap(); + private final HashMap handlers = new HashMap(); + private final HashMap sequenceNumbers = new HashMap(); + + // Broken SPUs disposer + private final Thread killer; + + public SubscribeProcessingThread(Processor processor) { + this.processor = processor; + + setName("SEPA-Subscribe-Processor"); + + killer = new Thread() { + public void run() { + while (processor.isRunning()) { + try { + String spuid = processor.getSchedulerQueue().waitSpuid2Kill(); + unregisterHandler(spuids.get(spuid), spuid); + } catch (InterruptedException e) { + return; + } + } + } + }; + killer.setName("SEPA-SPU-Killer"); + } + + @Override + public void start() { + killer.start(); + super.start(); + } + + public void run() { + while (processor.isRunning()) { + try { + // Wait request... + ScheduledRequest request = processor.getSchedulerQueue().waitSubscribeUnsubscribeRequest(); + logger.debug(request); + + // Process request + Response response = null; + if (request.isSubscribeRequest()) + response = subscribe((InternalSubscribeRequest) request.getRequest()); + else if (request.isUnsubscribeRequest()) + response = unsubscribe(((InternalUnsubscribeRequest) request.getRequest()).getSpuid()); + + // Send back response + processor.getSchedulerQueue().addResponse(request.getToken(), response); + + } catch (InterruptedException e) { + killer.interrupt(); + return; + } + } + } + + private synchronized Response subscribe(InternalSubscribeRequest req) { + EventHandler eventHandler = req.getEventHandler(); + String sparql = req.getSparql(); + String alias = req.getAlias(); + String defaultGraph = req.getDefaultGraphUri(); + String namedGraph = req.getNamedGraphUri(); + InternalSubscribeRequest wrappedRequest = new InternalSubscribeRequest(sparql, alias, defaultGraph, namedGraph, + this); + + // Get an SPU from the SPU manager (already available or a new one) + SPU spu = processor.getSPUManager().getSPU(wrappedRequest); + if (spu == null) + return new ErrorResponse(500, "Failed to create SPU"); + + // Generate a fake SPU id + String spuid = processor.getSPUManager().generateSpuid(); + + // Register handler + registerHandler(spu.getUUID(), spuid, eventHandler); + + return new SubscribeResponse(spuid, req.getAlias(), spu.getLastBindings()); + } + + private synchronized Response unsubscribe(String spuid) { + String masterSpuid = spuids.get(spuid); + + logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); + + if (masterSpuid == null) + return new ErrorResponse(404, "SPUID not found: " + spuid); + + // Unregister handler + unregisterHandler(masterSpuid, spuid); + + return new UnsubscribeResponse(spuid); + } + + public synchronized void killSpu(String spuid) { + unregisterHandler(spuids.get(spuid), spuid); + } + + @Override + public void notifyEvent(Notification notify) { + synchronized (handlers) { + logger.debug("@notifyEvent: " + notify); + + String spuid = notify.getSpuid(); + + ArrayList toBeKilled = new ArrayList(); + + if (activeSpus.containsKey(spuid)) { + for (String client : activeSpus.get(spuid)) { + try { + // Dispatching events + Notification event = new Notification(client, notify.getARBindingsResults(), + sequenceNumbers.get(client)); + handlers.get(client).notifyEvent(event); + sequenceNumbers.put(client, sequenceNumbers.get(client) + 1); + } catch (Exception e) { + logger.error("@notifyEvent Client:"+client+" Notification: "+notify+" Exception:"+e.getMessage()); + + // Handler is gone: unregister it + toBeKilled.add(client); + } + } + + for (String client : toBeKilled) + unregisterHandler(spuid, client); + } + +// if (activeSpus.get(spuid) == null) { +// // Deactivate SPU +// processor.getSPUManager().deactivate(spuid); +// return; +// } +// +// ArrayList toBeKilled = new ArrayList(); +// for (String client : activeSpus.get(spuid)) { +// if (handlers.get(client) != null) { +// // Dispatching events +// Notification event = new Notification(client, notify.getARBindingsResults(), +// sequenceNumbers.get(client)); +// handlers.get(client).notifyEvent(event); +// sequenceNumbers.put(client, sequenceNumbers.get(client) + 1); +// } else { +// toBeKilled.add(client); +// } +// } +// for (String client : toBeKilled) +// unregisterHandler(spuid, client); + } + } + + private synchronized void registerHandler(String masterSpuid, String spuid, EventHandler handler) { + synchronized (handlers) { + logger.debug("Register SPU handler: " + spuid); + + if (activeSpus.get(masterSpuid) == null) + activeSpus.put(masterSpuid, new ArrayList()); + + activeSpus.get(masterSpuid).add(spuid); + + handlers.put(spuid, handler); + + sequenceNumbers.put(spuid, 1); + spuids.put(spuid, masterSpuid); + } + } + + private void unregisterHandler(String masterSpuid, String spuid) { + synchronized (handlers) { + logger.debug("Unregister SPU handler: " + spuid); + + spuids.remove(spuid); + sequenceNumbers.remove(spuid); + handlers.remove(spuid); + + // SPUids + activeSpus.get(masterSpuid).remove(spuid); + logger.debug(masterSpuid + " number of clients: " + activeSpus.get(masterSpuid).size()); + if (activeSpus.get(masterSpuid).isEmpty()) { + activeSpus.remove(masterSpuid); + + // Deactivate SPU + processor.getSPUManager().deactivate(masterSpuid); + } + } + } + + @Override + public long getUpdateRequests() { + return SubscribeProcessorBeans.getUpdateRequests(); + } + + @Override + public long getSPUs_current() { + return SubscribeProcessorBeans.getSPUs_current(); + } + + @Override + public long getSPUs_max() { + return SubscribeProcessorBeans.getSPUs_max(); + } + + @Override + public float getSPUs_time() { + return SubscribeProcessorBeans.getSPUs_time(); + } + + @Override + public void reset() { + SubscribeProcessorBeans.reset(); + } + + @Override + public float getSPUs_time_min() { + return SubscribeProcessorBeans.getSPUs_time_min(); + } + + @Override + public float getSPUs_time_max() { + return SubscribeProcessorBeans.getSPUs_time_max(); + } + + @Override + public float getSPUs_time_average() { + return SubscribeProcessorBeans.getSPUs_time_averaae(); + } + + @Override + public long getSubscribeRequests() { + return SubscribeProcessorBeans.getSubscribeRequests(); + } + + @Override + public long getUnsubscribeRequests() { + return SubscribeProcessorBeans.getUnsubscribeRequests(); + } + + @Override + public long getSPUProcessingTimeout() { + return SubscribeProcessorBeans.getSPUProcessingTimeout(); + } + + @Override + public void setSPUProcessingTimeout(long t) { + SubscribeProcessorBeans.setActiveSPUs(t); + } + + @Override + public void scale_ms() { + SubscribeProcessorBeans.scale_ms(); + } + + @Override + public void scale_us() { + SubscribeProcessorBeans.scale_us(); + } + + @Override + public void scale_ns() { + SubscribeProcessorBeans.scale_ns(); + } + + @Override + public String getUnitScale() { + return SubscribeProcessorBeans.getUnitScale(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java similarity index 92% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java index 7d3b0041..9f642687 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessorMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.processing; -public interface SubscribeProcessorMBean { +public interface SubscribeProcessingThreadMBean { public long getUpdateRequests(); public long getSubscribeRequests(); public long getUnsubscribeRequests(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java deleted file mode 100644 index cb12aae0..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessor.java +++ /dev/null @@ -1,328 +0,0 @@ -/* This class implements the manager of the Semantic Processing Units (SPUs) of the Semantic Event Processing Architecture (SEPA) Engine - * - * Author: Luca Roffia (luca.roffia@unibo.it) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package it.unibo.arces.wot.sepa.engine.processing; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.concurrent.Semaphore; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; - -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; - -import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; - -import it.unibo.arces.wot.sepa.engine.core.EngineProperties; -import it.unibo.arces.wot.sepa.engine.core.EventHandler; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUNaive; -import it.unibo.arces.wot.sepa.timing.Timings; - -class SubscribeProcessor implements SubscribeProcessorMBean, EventHandler { - private final Logger logger = LogManager.getLogger(); - - private SPARQL11Properties endpointProperties; - private Semaphore endpointSemaphore; - - private SPUManager spuManager = new SPUManager(); - - // Maps - private HashMap> activeSpus = new HashMap>(); - private HashMap spuids = new HashMap(); - - private HashMap handlers = new HashMap(); - private HashMap sequenceNumbers = new HashMap(); - - public SubscribeProcessor(SPARQL11Properties endpointProperties, EngineProperties engineProperties, - Semaphore endpointSemaphore) { - this.endpointProperties = endpointProperties; - this.endpointSemaphore = endpointSemaphore; - - SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); - SubscribeProcessorBeans.setSPUProcessingTimeout(engineProperties.getSPUProcessingTimeout()); - } - - public void start() { - spuManager.start(); - } - - public void stop() { - spuManager.stop(); - } - - @Override - public void sendResponse(Response response) throws IOException { - logger.warn("Not implemented: " + response); - } - - @Override - public void notifyEvent(Notification notify) throws IOException { - synchronized (handlers) { - if (!activeSpus.containsKey(notify.getSpuid())) - return; - - // Notify all subscribed clients - ArrayList toBeRemoved = new ArrayList(); - for (String spuid : activeSpus.get(notify.getSpuid())) { - EventHandler handler = handlers.get(spuid); - - if (handler != null) { - logger.debug("Notify: " + spuid); - handler.notifyEvent( - new Notification(spuid, notify.getARBindingsResults(), sequenceNumbers.get(spuid))); - sequenceNumbers.put(spuid, sequenceNumbers.get(spuid) + 1); - } else { - logger.debug("Unregister SPU handler: " + spuid); - - spuids.remove(spuid); - sequenceNumbers.remove(spuid); - handlers.remove(spuid); - - toBeRemoved.add(spuid); - } - } - - // Remove SPUID - for (String spuid : toBeRemoved) { - activeSpus.get(notify.getSpuid()).remove(spuid); - logger.debug(notify.getSpuid() + " number of clients: " + activeSpus.get(notify.getSpuid()).size()); - if (activeSpus.get(notify.getSpuid()).isEmpty()) { - activeSpus.remove(notify.getSpuid()); - - // Deactivate SPU - spuManager.deactivate(notify.getSpuid()); - } - } - } - } - - public synchronized void process(UpdateResponse update) { - logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); - long start = Timings.getTime(); - - // Start subscription processing - spuManager.startProcessing(update); - - // Wait all SPUs completing processing (or timeout) - spuManager.waitEndOfProcessing(); - - long stop = Timings.getTime(); - - SubscribeProcessorBeans.timings(start, stop); - - logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); - } - - public synchronized Response subscribe(SubscribeRequest req, EventHandler handler) { - logger.trace(req.toString()); - - SubscribeProcessorBeans.subscribeRequest(); - - // Is SPU already available or do we need to create a new one? - ISPU spu = spuManager.getSPU(req); - if (spu == null) - spu = createSPU(req); - if (spu == null) - return new ErrorResponse(req.getToken(), 500, "Failed to create SPU " + req.toString()); - - // Generate a fake SPU id - String spuid = spuManager.generateSpuid(); - - // Register handler - registerHandler(spu.getUUID(), spuid, handler); - - return new SubscribeResponse(req.getToken(), spuid, spu.getLastBindings()); - } - - public synchronized Response unsubscribe(UnsubscribeRequest req) { - logger.trace(req); - - SubscribeProcessorBeans.unsubscribeRequest(); - - String spuid = req.getSubscribeUUID(); - String masterSpuid = spuids.get(spuid); - - logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); - - if (masterSpuid == null) - return new ErrorResponse(req.getToken(), 404, "SPUID not found: " + spuid); - - // Unregister handler - unregisterHandler(masterSpuid, spuid); - - return new UnsubscribeResponse(req.getToken(), spuid); - } - - // TODO: choose different kinds of SPU based on subscribe request - private ISPU createSPU(SubscribeRequest req) { - ISPU spu; - - try { - spu = new SPUNaive(req, this, endpointProperties, endpointSemaphore, spuManager); - } catch (SEPAProtocolException e) { - logger.debug("SPU creation failed: " + e.getMessage()); - return null; - } - - // Initialize SPU - Response init = spu.init(); - if (init.isError()) { - logger.error("SPU initialization failed"); - return null; - } - - logger.debug("Add SPU to activation queue"); - - // Request SPU activation - if (!spuManager.activate(spu, req)) - return null; - - return spu; - } - - private void registerHandler(String masterSpuid, String spuid, EventHandler handler) { - synchronized (handlers) { - logger.debug("Register SPU handler: " + spuid); - - if (activeSpus.get(masterSpuid) == null) - activeSpus.put(masterSpuid, new ArrayList()); - - activeSpus.get(masterSpuid).add(spuid); - - handlers.put(spuid, handler); - - sequenceNumbers.put(spuid, 1); - spuids.put(spuid, masterSpuid); - } - } - - private void unregisterHandler(String masterSpuid, String spuid) { - synchronized (handlers) { - logger.debug("Unregister SPU handler: " + spuid); - - spuids.remove(spuid); - sequenceNumbers.remove(spuid); - handlers.remove(spuid); - - // SPUids - activeSpus.get(masterSpuid).remove(spuid); - logger.debug(masterSpuid + " number of clients: " + activeSpus.get(masterSpuid).size()); - if (activeSpus.get(masterSpuid).isEmpty()) { - activeSpus.remove(masterSpuid); - - // Deactivate SPU - spuManager.deactivate(masterSpuid); - } - } - } - - @Override - public long getUpdateRequests() { - return SubscribeProcessorBeans.getUpdateRequests(); - } - - @Override - public long getSPUs_current() { - return SubscribeProcessorBeans.getSPUs_current(); - } - - @Override - public long getSPUs_max() { - return SubscribeProcessorBeans.getSPUs_max(); - } - - @Override - public float getSPUs_time() { - return SubscribeProcessorBeans.getSPUs_time(); - } - - @Override - public void reset() { - SubscribeProcessorBeans.reset(); - } - - @Override - public float getSPUs_time_min() { - return SubscribeProcessorBeans.getSPUs_time_min(); - } - - @Override - public float getSPUs_time_max() { - return SubscribeProcessorBeans.getSPUs_time_max(); - } - - @Override - public float getSPUs_time_average() { - return SubscribeProcessorBeans.getSPUs_time_averaae(); - } - - @Override - public long getSubscribeRequests() { - return SubscribeProcessorBeans.getSubscribeRequests(); - } - - @Override - public long getUnsubscribeRequests() { - return SubscribeProcessorBeans.getUnsubscribeRequests(); - } - - @Override - public long getSPUProcessingTimeout() { - return SubscribeProcessorBeans.getSPUProcessingTimeout(); - } - - @Override - public void setSPUProcessingTimeout(long t) { - SubscribeProcessorBeans.setActiveSPUs(t); - } - - @Override - public void scale_ms() { - SubscribeProcessorBeans.scale_ms(); - } - - @Override - public void scale_us() { - SubscribeProcessorBeans.scale_us(); - } - - @Override - public void scale_ns() { - SubscribeProcessorBeans.scale_ns(); - } - - @Override - public String getUnitScale() { - return SubscribeProcessorBeans.getUnitScale(); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java index ed4964ab..293d8e6d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java @@ -1,66 +1,41 @@ package it.unibo.arces.wot.sepa.engine.processing; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerRequestResponseQueue; - -public class UpdateProcessingThread extends Thread { - private UpdateProcessor updateProcessor; - private SchedulerRequestResponseQueue schedulerQueue; - private SubscribeProcessor subscribeProcessor; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUpdateRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; - private LinkedBlockingQueue updateQueue = new LinkedBlockingQueue(); - - private final AtomicBoolean end = new AtomicBoolean(false); +class UpdateProcessingThread extends Thread { + private final Processor processor; - public UpdateProcessingThread(UpdateProcessor updateProcessor, SubscribeProcessor subscribeProcessor, - SchedulerRequestResponseQueue queue) { - this.updateProcessor = updateProcessor; - this.schedulerQueue = queue; - this.subscribeProcessor = subscribeProcessor; - - setName("SEPA-Update-Scheduler"); + public UpdateProcessingThread(Processor processor) { + this.processor = processor; + setName("SEPA-Update-Processor"); } public void run() { - while (!end.get()) { - UpdateRequest request; + while (processor.isRunning()) { + ScheduledRequest request; try { - request = updateQueue.take(); + request = processor.getSchedulerQueue().waitUpdateRequest(); } catch (InterruptedException e) { return; } + // Update request + InternalUpdateRequest update = (InternalUpdateRequest)request.getRequest(); + + // Notify update (not reliable) + if (!processor.isUpdateReilable()) processor.getSchedulerQueue().addResponse(request.getToken(),new UpdateResponse("Processing: "+update)); + // Process update request - Response ret = updateProcessor.process(request); + Response ret = processor.getUpdateProcessor().process(update); // Notify update result - synchronized(schedulerQueue) { - if (schedulerQueue != null) schedulerQueue.addResponse(ret); - } + if (processor.isUpdateReilable()) processor.getSchedulerQueue().addResponse(request.getToken(),ret); // Subscription processing - if (ret.isUpdateResponse()) { - subscribeProcessor.process((UpdateResponse) ret); - } - } - } - - public void finish(){ - end.set(true); - } - - public void process(UpdateRequest req) { - updateQueue.add(req); - } - - public void setSchedulerQueue(SchedulerRequestResponseQueue queue) { - synchronized(schedulerQueue) { - schedulerQueue = queue; + if (ret.isUpdateResponse()) processor.getSPUManager().process((UpdateResponse) ret); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index 60c84d82..40830427 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -34,14 +34,15 @@ import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.UpdateProcessorBeans; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUpdateRequest; import it.unibo.arces.wot.sepa.timing.Timings; -public class UpdateProcessor implements UpdateProcessorMBean { +class UpdateProcessor implements UpdateProcessorMBean { private static final Logger logger = LogManager.getLogger(); - private SPARQL11Protocol endpoint; - private Semaphore endpointSemaphore; - private SPARQL11Properties properties; + private final SPARQL11Protocol endpoint; + private final Semaphore endpointSemaphore; + private final SPARQL11Properties properties; public UpdateProcessor(SPARQL11Properties properties, Semaphore endpointSemaphore) throws SEPAProtocolException { endpoint = new SPARQL11Protocol(); @@ -51,7 +52,7 @@ public UpdateProcessor(SPARQL11Properties properties, Semaphore endpointSemaphor SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); } - public synchronized Response process(UpdateRequest req) { + public synchronized Response process(InternalUpdateRequest req) { long start = Timings.getTime(); if (endpointSemaphore != null) @@ -73,10 +74,10 @@ public synchronized Response process(UpdateRequest req) { // UPDATE the endpoint Response ret; - UpdateRequest request = new UpdateRequest(req.getToken(), properties.getUpdateMethod(), + UpdateRequest request = new UpdateRequest(properties.getUpdateMethod(), properties.getDefaultProtocolScheme(), properties.getDefaultHost(), properties.getDefaultPort(), - properties.getUpdatePath(), req.getSPARQL(), UpdateProcessorBeans.getTimeout(), req.getUsingGraphUri(), - req.getUsingNamedGraphUri(), authorizationHeader); + properties.getUpdatePath(), req.getSparql(), req.getDefaultGraphUri(), + req.getNamedGraphUri(), authorizationHeader,UpdateProcessorBeans.getTimeout()); logger.trace(request); ret = endpoint.update(request); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java index d6831a4d..e68881ca 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java @@ -4,14 +4,12 @@ import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -public interface ISPU extends Runnable { +interface ISPU { Response init(); BindingsResults getLastBindings(); - void terminate(); - String getUUID(); void process(UpdateResponse res); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index 5a94973a..6142a09b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -18,26 +18,20 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -import java.io.IOException; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Semaphore; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; -import it.unibo.arces.wot.sepa.engine.processing.QueryProcessor; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; - import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; -import it.unibo.arces.wot.sepa.engine.core.EventHandler; /** * This class represents a Semantic Processing Unit (SPU) @@ -47,25 +41,19 @@ * @version 0.1 */ -// public abstract class SPU extends Observable implements Runnable { -abstract class SPU implements ISPU { +public abstract class SPU extends Thread implements ISPU { private final Logger logger; // The URI of the subscription (i.e., sepa://spuid/UUID) private String uuid = null; // Update queue - protected ConcurrentLinkedQueue updateQueue = new ConcurrentLinkedQueue(); - - protected QueryProcessor queryProcessor; + private final LinkedBlockingQueue updateQueue = new LinkedBlockingQueue(); - protected SubscribeRequest request; - - // Handler of notifications - protected EventHandler handler; + protected final InternalSubscribeRequest subscribe; // Thread loop - private boolean running = true; + private final AtomicBoolean running = new AtomicBoolean(true); // Last bindings results protected BindingsResults lastBindings = null; @@ -74,45 +62,29 @@ abstract class SPU implements ISPU { private Response notify; // List of processing SPU - private SPUManager manager; - - public SPU(SubscribeRequest subscribe, SPARQL11Properties properties, EventHandler eventHandler, - Semaphore endpointSemaphore, SPUManager manager) throws SEPAProtocolException { - if (eventHandler == null) - throw new SEPAProtocolException(new IllegalArgumentException("Subscribe event handler is null")); - if (manager == null) - throw new SEPAProtocolException(new IllegalArgumentException("SPU manager is null")); + protected SPUManager manager; + public SPU(InternalSubscribeRequest subscribe, SPUManager manager) { this.manager = manager; - + this.subscribe = subscribe; + uuid = manager.generateSpuid(); logger = LogManager.getLogger("SPU" + uuid); - - request = subscribe; - handler = eventHandler; - - queryProcessor = new QueryProcessor(properties, endpointSemaphore); - - running = true; } - public SubscribeRequest getSubscribe() { - return request; + public InternalSubscribeRequest getSubscribe() { + return subscribe; } - public abstract Response processInternal(UpdateResponse update, int timeout); + public abstract Response processInternal(UpdateResponse update); @Override public BindingsResults getLastBindings() { return lastBindings; } - @Override - public void terminate() { - synchronized (updateQueue) { - running = false; - updateQueue.notify(); - } + public void finish() { + running.set(false); } @Override @@ -129,48 +101,44 @@ public String getUUID() { @Override public void process(UpdateResponse res) { - synchronized (updateQueue) { - updateQueue.offer(res); - updateQueue.notify(); + try { + updateQueue.put(res); + } catch (InterruptedException e) { + } } - + @Override public void run() { - while (running) { + while (running.get()) { // Poll the request from the queue UpdateResponse updateResponse; - while ((updateResponse = updateQueue.poll()) != null && running) { - // Processing update - logger.debug("* PROCESSING *"); - - // Asynchronous processing and waiting for result - notify = processInternal(updateResponse, SubscribeProcessorBeans.getSPUProcessingTimeout()); - - // Notify event handler - if (notify.isNotification()) - try { - handler.notifyEvent((Notification) notify); - } catch (IOException e) { - logger.error("Failed to notify " + notify); - } - else - logger.debug("Not a notification: " + notify); - - // Notify SPU manager - logger.debug("Notify SPU manager. Running: " + running); - manager.endProcessing(this); + try { + updateResponse = updateQueue.take(); + } catch (InterruptedException e1) { + return; } - // Wait next request... - if (running) - synchronized (updateQueue) { - try { - updateQueue.wait(); - } catch (InterruptedException e) { - return; - } + // Processing update + logger.debug("* PROCESSING *"); + + // Asynchronous processing and waiting for result + notify = processInternal(updateResponse); + + // Notify event handler + if (notify.isNotification()) + try { + subscribe.getEventHandler().notifyEvent((Notification) notify); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); } + else + logger.debug("Not a notification: " + notify); + + // Notify SPU manager + logger.debug("Notify SPU manager. Running: " + running); + manager.endProcessing(this); + } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index d1f94605..a3cce5cc 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -1,10 +1,14 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; +import it.unibo.arces.wot.sepa.engine.processing.Processor; +import it.unibo.arces.wot.sepa.engine.processing.QueryProcessor; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import it.unibo.arces.wot.sepa.timing.Timings; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -20,145 +24,144 @@ public class SPUManager { private final Logger logger = LogManager.getLogger(); - // SPUID ==> SPU - private final HashMap spus = new HashMap<>(); + // SPUID ==> SPU + private final HashMap spus = new HashMap(); // Request ==> SPU - private final HashMap request2Spu = new HashMap<>(); - + private final HashMap request2Spu = new HashMap(); + // SPUID ==> Request - private final HashMap spuid2Request = new HashMap<>(); - + private final HashMap spuid2Request = new HashMap(); + // SPUs processing set - private final HashSet processingSpus = new HashSet<>(); + private final HashSet processingSpus = new HashSet(); - private final Subscriber subscriber; - private final Unsubscriber unsubscriber; - - public SPUManager() { - this.subscriber = new Subscriber(this); - this.unsubscriber = new Unsubscriber(this); + private final Processor processor; + + public SPUManager(Processor processor) { + this.processor = processor; } - public void start() { - this.subscriber.start(); - this.unsubscriber.start(); + public QueryProcessor getQueryProcessor() { + return processor.getQueryProcessor(); } - public void stop() { - this.subscriber.finish(); - this.unsubscriber.finish(); - - this.subscriber.interrupt(); - this.unsubscriber.interrupt(); - } - public String generateSpuid() { return "sepa://spuid/" + UUID.randomUUID().toString(); } - public void startProcessing(UpdateResponse update) { - synchronized (processingSpus) { - processingSpus.clear(); + public synchronized void process(UpdateResponse update) { + logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); + long start = Timings.getTime(); - Iterator spus = filter(update); + processingSpus.clear(); - while (spus.hasNext()) { - ISPU spu = spus.next(); - processingSpus.add(spu); - spu.process(update); - } + Iterator spus = filter(update); - logger.debug("Activated SPUs: " + processingSpus.size()); + while (spus.hasNext()) { + SPU spu = spus.next(); + processingSpus.add(spu); + spu.process(update); } - } - public void waitEndOfProcessing() { - // Wait all SPUs completing processing (or timeout) - synchronized (processingSpus) { - while (!processingSpus.isEmpty()) { - logger.debug(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); - try { - processingSpus.wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); - } catch (InterruptedException e) { - return; - } - } - // TIMEOUT - if (!processingSpus.isEmpty()) { - logger.error("Timeout on SPU processing. SPUs still running: " + processingSpus.size()); + logger.debug("Activated SPUs: " + processingSpus.size()); + + while (!processingSpus.isEmpty()) { + logger.debug(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); + try { + wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); + } catch (InterruptedException e) { + return; } } - } - - public void endProcessing(SPU s) { - synchronized (processingSpus) { - processingSpus.remove(s); - logger.debug("SPUs left: " + processingSpus.size()); - processingSpus.notify(); + // TIMEOUT + if (!processingSpus.isEmpty()) { + logger.error("Timeout on SPU processing. SPUs still running: " + processingSpus.size()); } - } - public boolean isEmpty() { - return processingSpus.isEmpty(); - } + long stop = Timings.getTime(); - public synchronized void register(ISPU spu, SubscribeRequest request) { - logger.debug("Register SPU: "+spu.getUUID()); - - spus.put(spu.getUUID(), spu); - request2Spu.put(request, spu); - spuid2Request.put(spu.getUUID(), request); - } + SubscribeProcessorBeans.timings(start, stop); - public synchronized void unRegister(String spuID) { - if (!isValidSpuId(spuID)) { - throw new IllegalArgumentException("Unregistering a not existing SPUID: " + spuID); - } - - spus.get(spuID).terminate(); - - spus.remove(spuID); - request2Spu.remove(spuid2Request.get(spuID)); - spuid2Request.remove(spuID); + logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); } - private boolean isValidSpuId(String id) { - return spus.containsKey(id); + public synchronized void endProcessing(SPU s) { + logger.debug("EOP: " + s.getUUID()); + processingSpus.remove(s); + notify(); } - public synchronized Iterator filter(UpdateResponse response) { + private Iterator filter(UpdateResponse response) { return spus.values().iterator(); } - public synchronized Collection getAll() { - return spus.values(); - } + + public synchronized SPU getSPU(InternalSubscribeRequest req) { + if (request2Spu.containsKey(req)) + return request2Spu.get(req); + + // Create SPU + SPU spu= createSPU(req,this); + if (spu == null) { + logger.error("SPU creation failed: "+req); + return spu; + } - public synchronized int size() { - return spus.values().size(); - } + // Initialize SPU + Response init = spu.init(); + if (init.isError()) { + logger.error("SPU initialization failed"); + return null; + } - public synchronized ISPU getSPU(SubscribeRequest req) { - return request2Spu.get(req); - } + // Activate SPU + activate(spu, req); - public boolean activate(ISPU spu, SubscribeRequest req) { + return spu; + } + + // TODO: choose different kinds of SPU based on subscribe request + private SPU createSPU(InternalSubscribeRequest req,SPUManager manager) { try { - subscriber.activate(spu, req); - } catch (InterruptedException e) { - return false; + return new SPUNaive(req, this); + } catch (SEPAProtocolException e) { + return null; } - return true; } - public boolean deactivate(String spuid) { - try { - unsubscriber.deactivate(spuid); - } catch (InterruptedException e) { - return false; - } - return true; + public synchronized void activate(SPU spu, InternalSubscribeRequest request) { + // Start the SPU thread + logger.trace("Starting SPU: " + spu.getUUID()); + spu.setName("SPU_" + spu.getUUID()); + spu.start(); + + // Register the SPU as ACTIVE + logger.trace("Registering SPU: " + spu.getUUID()); + spus.put(spu.getUUID(), spu); + request2Spu.put(request, spu); + spuid2Request.put(spu.getUUID(), request); + + SubscribeProcessorBeans.setActiveSPUs(spus.values().size()); + logger.debug(spu.getUUID() + " ACTIVATED (total: " + spus.values().size() + ")"); } + public synchronized void deactivate(String spuid) { + if (!spus.containsKey(spuid)) { + logger.warn("Unregistering a not existing SPUID: " + spuid); + return; + } + + // Stop SPU + spus.get(spuid).finish(); + spus.get(spuid).interrupt(); + + // Remove from active SPUs + spus.remove(spuid); + request2Spu.remove(spuid2Request.get(spuid)); + spuid2Request.remove(spuid); + + SubscribeProcessorBeans.setActiveSPUs(spus.values().size()); + logger.debug("Active SPUs: " + spus.values().size()); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index 13577542..f04e9984 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -21,8 +21,6 @@ import org.apache.logging.log4j.Logger; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; @@ -32,18 +30,15 @@ import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.core.EventHandler; - -import java.util.concurrent.Semaphore; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; import org.apache.logging.log4j.LogManager; -public class SPUNaive extends SPU { +class SPUNaive extends SPU { private final Logger logger; - public SPUNaive(SubscribeRequest subscribe, EventHandler handler, SPARQL11Properties endpointProperties, - Semaphore endpointSemaphore, SPUManager sync) throws SEPAProtocolException { - super(subscribe, endpointProperties, handler, endpointSemaphore, sync); + public SPUNaive(InternalSubscribeRequest subscribe, SPUManager manager) throws SEPAProtocolException { + super(subscribe, manager); logger = LogManager.getLogger("SPUNaive" + getUUID()); logger.debug("SPU: " + this.getUUID() + " request: " + subscribe); @@ -51,10 +46,10 @@ public SPUNaive(SubscribeRequest subscribe, EventHandler handler, SPARQL11Proper @Override public Response init() { - logger.debug("Process SPARQL query " + request); + logger.debug("PROCESS " + subscribe); // Process the SPARQL query - Response ret = queryProcessor.process(request); + Response ret = manager.getQueryProcessor().process(subscribe); if (ret.getClass().equals(ErrorResponse.class)) { logger.error("Not initialized"); @@ -65,18 +60,17 @@ public Response init() { logger.debug("First results: " + lastBindings.toString()); - return new SubscribeResponse(request.getToken(), getUUID(), request.getAlias(), lastBindings); + return new SubscribeResponse(getUUID(), subscribe.getAlias(), lastBindings); } @Override - public Response processInternal(UpdateResponse update,int timeout) { - logger.debug("* PROCESSING *" + request); + public Response processInternal(UpdateResponse update) { + logger.debug("* PROCESSING *" + subscribe); Response ret; try { // Query the SPARQL processing service - request.setTimeout(timeout); - ret = queryProcessor.process(request); + ret = manager.getQueryProcessor().process(subscribe); if (ret.getClass().equals(ErrorResponse.class)) { logger.error(ret); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java deleted file mode 100644 index 3db20808..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java +++ /dev/null @@ -1,60 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; - -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -class Subscriber extends Thread { - private final Logger logger = LogManager.getLogger(); - private final AtomicBoolean end = new AtomicBoolean(false); - - private final BlockingQueue subscriptionQueue = new LinkedBlockingQueue(); - private final BlockingQueue requestQueue = new LinkedBlockingQueue(); - - private final SPUManager spuManager; - - public Subscriber(SPUManager manager){ - super("SEPA-SPU-Subscriber"); - spuManager = manager; - } - - @Override - public void run() { - while (!end.get()) { - try { - // Wait for a new SPU to be activated - ISPU spu = subscriptionQueue.take(); - SubscribeRequest request = requestQueue.take(); - - // Start the SPU thread - logger.debug("Starting SPU: "+spu.getUUID()); - Thread th = new Thread(spu); - th.setName("SPU_" + spu.getUUID()); - th.start(); - logger.debug("Started SPU: "+spu.getUUID()); - - spuManager.register(spu,request); - - SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); - logger.debug(spu.getUUID() + " ACTIVATED (total: " + spuManager.size() + ")"); - } catch (InterruptedException e) { - logger.debug(e); - } - } - } - - public void activate(ISPU spu,SubscribeRequest request) throws InterruptedException { - subscriptionQueue.put(spu); - requestQueue.put(request); - } - - public void finish(){ - end.set(true); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java deleted file mode 100644 index f3c54307..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Unsubscriber.java +++ /dev/null @@ -1,57 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; - -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Unsubscriber thread. It loops over Unsubcribe Request queue and removes the SPU from - * SpuManager - * - * @see SPUManager - */ -class Unsubscriber extends Thread { - private final Logger logger = LogManager.getLogger(); - private final BlockingQueue unsubscribeQueue = new LinkedBlockingQueue(); - private final SPUManager spuManager; - private final AtomicBoolean end = new AtomicBoolean(false); - - public Unsubscriber(SPUManager manager){ - super("SEPA-SPU-Unsubscriber"); - spuManager = manager; - } - - @Override - public void run() { - while (!end.get()) { - String spuUID = null; - try { - spuUID = unsubscribeQueue.take(); - logger.debug("Terminating: " + spuUID); - - spuManager.unRegister(spuUID); - - SubscribeProcessorBeans.setActiveSPUs(spuManager.size()); - logger.debug("Active SPUs: " + spuManager.size()); - } catch (InterruptedException e) { - logger.debug(e); - } - } - } - - public void deactivate(String spuid) throws InterruptedException { - logger.debug("Deactivate: "+spuid); - unsubscribeQueue.put(spuid); - } - - public void finish(){ - end.set(true); - } -} - - diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java index f9fa1c1a..328786b2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java @@ -9,10 +9,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalQueryRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUQRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; /** @@ -31,7 +30,7 @@ public QueryHandler(Scheduler scheduler) throws IllegalArgumentException { } @Override - protected Request parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolException { + protected InternalUQRequest parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolException { switch (exchange.getRequest().getRequestLine().getMethod().toUpperCase()) { case "GET": /*
    @@ -75,7 +74,7 @@ protected Request parse(HttpAsyncExchange exchange) throws SPARQL11ProtocolExcep
     				String graphUri = params.get("default-graph-uri");
     				String namedGraphUri = params.get("named-graph-uri");
     
    -				return new QueryRequest(sparql, graphUri, namedGraphUri);
    +				return new InternalQueryRequest(sparql, graphUri, namedGraphUri);
     			} catch (Exception e) {
     				throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, e.getMessage());
     			}
    diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java
    index 3091c8b4..e67a569a 100644
    --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java
    +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java
    @@ -22,15 +22,16 @@
     import org.apache.logging.log4j.LogManager;
     import org.apache.logging.log4j.Logger;
     
    -import it.unibo.arces.wot.sepa.commons.request.QueryRequest;
    -import it.unibo.arces.wot.sepa.commons.request.Request;
    -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest;
     import it.unibo.arces.wot.sepa.engine.bean.HTTPHandlerBeans;
     import it.unibo.arces.wot.sepa.engine.bean.SEPABeans;
     import it.unibo.arces.wot.sepa.engine.dependability.CORSManager;
     import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities;
    +import it.unibo.arces.wot.sepa.engine.scheduling.InternalQueryRequest;
    +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUQRequest;
    +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUpdateRequest;
    +import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest;
     import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler;
    -import it.unibo.arces.wot.sepa.timing.Timings;
    +import it.unibo.arces.wot.sepa.engine.timing.Timings;
     
     public abstract class SPARQL11Handler implements HttpAsyncRequestHandler, SPARQL11HandlerMBean {
     	private static final Logger logger = LogManager.getLogger();
    @@ -74,7 +75,7 @@ protected boolean corsHandling(HttpAsyncExchange exchange) {
     		return true;
     	}
     
    -	protected abstract Request parse(HttpAsyncExchange exchange);
    +	protected abstract InternalUQRequest parse(HttpAsyncExchange exchange);
     
     	/**
     	 *  SPARQL 1.1 Protocol
    @@ -102,7 +103,7 @@ protected boolean corsHandling(HttpAsyncExchange exchange) {
     	 * 
    * */ - protected Request parsePost(HttpAsyncExchange exchange, String type) { + protected InternalUQRequest parsePost(HttpAsyncExchange exchange, String type) { String contentTypePost = "application/sparql-query"; String defGraph = "default-graph-uri"; String namedGraph = "named-graph-uri"; @@ -164,9 +165,9 @@ protected Request parsePost(HttpAsyncExchange exchange, String type) { } if (type.equals("query")) - return new QueryRequest(sparql, default_graph_uri, named_graph_uri); + return new InternalQueryRequest(sparql, default_graph_uri, named_graph_uri); else - return new UpdateRequest(sparql, default_graph_uri, named_graph_uri); + return new InternalUpdateRequest(sparql, default_graph_uri, named_graph_uri); } @Override @@ -184,7 +185,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont jmx.corsFailed(); return; } - Request sepaRequest = null; + InternalUQRequest sepaRequest = null; try { // Parsing SPARQL 1.1 request and attach a token @@ -198,25 +199,31 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont // Validate if (!validate(httpExchange.getRequest())) { - logger.error("Validation failed SPARQL: " + sepaRequest.getSPARQL()); + logger.error("Validation failed SPARQL: " + sepaRequest.getSparql()); HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, - "Validation failed SPARQL: " + sepaRequest.getSPARQL()); + "Validation failed SPARQL: " + sepaRequest.getSparql()); jmx.validatingFailed(); return; } // Authorize if (!authorize(httpExchange.getRequest())) { - logger.error("Authorization failed SPARQL: " + sepaRequest.getSPARQL()); + logger.error("Authorization failed SPARQL: " + sepaRequest.getSparql()); HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_UNAUTHORIZED, - "Authorization failed SPARQL: " + sepaRequest.getSPARQL()); + "Authorization failed SPARQL: " + sepaRequest.getSparql()); jmx.authorizingFailed(); return; } // Schedule request Timings.log(sepaRequest); - scheduler.schedule(sepaRequest, new SPARQL11ResponseHandler(httpExchange, jmx)); + ScheduledRequest req = scheduler.schedule(sepaRequest,new SPARQL11ResponseHandler(httpExchange, jmx)); + if (req == null) { + logger.error("Out of tokens"); + HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_NOT_ACCEPTABLE, + "Too many pending requests"); + jmx.outOfTokens(); + } } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java index f615bbb4..7473ae59 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java @@ -36,7 +36,7 @@ public void sendResponse(Response response) { Timings.log(response); jmx.stop(handler); - logger.debug("Response sent #"+response.getToken()); + logger.trace(response); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java index 812b0820..0c9ec586 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java @@ -5,7 +5,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.commons.request.Request; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUQRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class UpdateHandler extends SPARQL11Handler { @@ -16,7 +16,7 @@ public UpdateHandler(Scheduler scheduler) throws IllegalArgumentException { } @Override - protected Request parse(HttpAsyncExchange exchange) { + protected InternalUQRequest parse(HttpAsyncExchange exchange) { if (!exchange.getRequest().getRequestLine().getMethod().toUpperCase().equals("POST")) { logger.error("Request MUST conform to SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); throw new SPARQL11ProtocolException(HttpStatus.SC_BAD_REQUEST, diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index 0ec83e80..f907e0cf 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -18,13 +18,16 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; + import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; + +import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureWebsocketServer extends WebsocketServer implements SecureWebsocketServerMBean { @@ -54,7 +57,7 @@ public SecureWebsocketServer(int port, String path, Scheduler scheduler, Authori } @Override - protected Request parseRequest(String request, WebSocket conn) + protected InternalRequest parseRequest(String request, WebSocket conn) throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException { JsonObject req; @@ -66,7 +69,7 @@ protected Request parseRequest(String request, WebSocket conn) Response ret = validateToken(auth); if (ret.isError()) { // Not authorized - jmx.onNotAuthorizedRequest(); + WebsocketBeans.onNotAuthorizedRequest(); logger.warn("NOT AUTHORIZED"); conn.send(ret.toString()); @@ -101,20 +104,20 @@ protected Request parseRequest(String request, WebSocket conn) } catch(Exception e) {} - return new SubscribeRequest(sparql,alias,defaultGraphUri,namedGraphUri,auth); + return new InternalSubscribeRequest(sparql,alias,defaultGraphUri,namedGraphUri,activeSockets.get(conn)); } else if (req.has("unsubscribe")) { Response ret = validateToken( req.get("unsubscribe").getAsJsonObject().get("authorization").getAsString()); if (ret.isError()) { // Not authorized - jmx.onNotAuthorizedRequest(); + WebsocketBeans.onNotAuthorizedRequest(); logger.warn("NOT AUTHORIZED"); conn.send(ret.toString()); return null; } - return new UnsubscribeRequest(req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString()); + return new InternalUnsubscribeRequest(req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString()); } } catch (Exception e) { logger.debug(e.getLocalizedMessage()); @@ -164,7 +167,7 @@ public void onStart() { @Override public long getNotAuthorized() { - return jmx.getNotAuthorized(); + return WebsocketBeans.getNotAuthorized(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java index d32daeb9..0bc3cf6c 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java @@ -1,77 +1,63 @@ package it.unibo.arces.wot.sepa.engine.protocol.websocket; -import java.io.IOException; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.java_websocket.WebSocket; import org.java_websocket.exceptions.WebsocketNotConnectedException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; -//import it.unibo.arces.wot.sepa.commons.response.Ping; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; -import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; +import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; -import it.unibo.arces.wot.sepa.timing.Timings; -public class WebsocketEventHandler implements EventHandler { +public class WebsocketEventHandler implements EventHandler ,ResponseHandler { private static final Logger logger = LogManager.getLogger(); - private WebSocket socket; - private WebsocketBeans jmx; - private long start; + private final WebSocket socket; // Dependability manager - private DependabilityManager dependabilityMng; + private final DependabilityManager dependabilityMng; - public WebsocketEventHandler(WebSocket s,WebsocketBeans jmx,DependabilityManager dependabilityMng){ - socket = s; - this.jmx = jmx; + public WebsocketEventHandler(WebSocket s,DependabilityManager dependabilityMng){ + this.socket = s; this.dependabilityMng = dependabilityMng; } - private void send(Response ret) throws IOException { + private void send(Response ret) throws SEPAProtocolException { try{ socket.send(ret.toString()); } catch(WebsocketNotConnectedException e){ - throw new IOException(e.getMessage()); + logger.error("Socket: "+socket.hashCode()+" failed to send response: "+ret+" Exception:"+e.getMessage()); + throw new SEPAProtocolException(e.getMessage()); } } - public void startTiming() { - start = Timings.getTime(); - } - @Override - public void sendResponse(Response response) throws IOException { - long timing = 0; - + public void sendResponse(Response response) throws SEPAProtocolException { logger.trace(response); if (response.isSubscribeResponse()) { - timing = jmx.subscribeTimings(start); - dependabilityMng.onSubscribe(socket, ((SubscribeResponse)response).getSpuid()); - logger.debug("Response #"+response.getToken()+" ("+timing+" ms)"); + dependabilityMng.onSubscribe(socket.hashCode(), ((SubscribeResponse)response).getSpuid()); } else if (response.isUnsubscribeResponse()) { - timing = jmx.unsubscribeTimings(start); - dependabilityMng.onUnsubscribe(socket, ((UnsubscribeResponse)response).getSpuid()); - logger.debug("Response #"+response.getToken()+" ("+timing+" ms)"); + dependabilityMng.onUnsubscribe(socket.hashCode(), ((UnsubscribeResponse)response).getSpuid()); } else if (response.isError()) { - logger.debug("Response #"+response.getToken()); - logger.error(response); + logger.error(response); + dependabilityMng.onError(socket.hashCode(), (ErrorResponse)response); } send(response); } @Override - public void notifyEvent(Notification notify) throws IOException { + public void notifyEvent(Notification notify) throws SEPAProtocolException { send(notify); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index b3d532d7..23e9d0f4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -22,15 +22,17 @@ import com.google.gson.JsonSyntaxException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; + import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; -import it.unibo.arces.wot.sepa.timing.Timings; +import it.unibo.arces.wot.sepa.engine.timing.Timings; public class WebsocketServer extends WebSocketServer implements WebsocketServerMBean { private static final Logger logger = LogManager.getLogger(); @@ -46,16 +48,13 @@ protected String getWelcomeMessage() { private String path; // Fragmentation support - private HashMap fragmentedMessages = new HashMap(); - - // JMX - protected WebsocketBeans jmx = new WebsocketBeans(); + private final HashMap fragmentedMessages = new HashMap(); // Active sockets - private HashMap activeSockets = new HashMap(); + protected final HashMap activeSockets = new HashMap(); // Dependability manager - private DependabilityManager dependabilityMng; + private final DependabilityManager dependabilityMng; public WebsocketServer(int port, String path, Scheduler scheduler, DependabilityManager dependabilityMng) throws SEPAProtocolException { @@ -84,7 +83,7 @@ public WebsocketServer(int port, String path, Scheduler scheduler, Dependability @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { - logger.debug("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); + logger.trace("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); @@ -106,8 +105,8 @@ public void onClose(WebSocket conn, int code, String reason, boolean remote) { fragmentedMessages.remove(conn); - // Unsubscribe all SPUs - dependabilityMng.onBrokenSocket(conn); + // KILL ALL SPUs + dependabilityMng.onBrokenSocket(conn.hashCode()); // Remove active socket activeSockets.remove(conn); @@ -115,9 +114,9 @@ public void onClose(WebSocket conn, int code, String reason, boolean remote) { @Override public void onMessage(WebSocket conn, String message) { - jmx.onMessage(); + WebsocketBeans.onMessage(); - logger.debug("Message from: " + conn.getRemoteSocketAddress() + " [" + message + "]"); + logger.trace("Message from: " + conn.getRemoteSocketAddress() + " [" + message + "]"); if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); @@ -127,23 +126,29 @@ public void onMessage(WebSocket conn, String message) { return; } + // Add active socket + if (!activeSockets.containsKey(conn)) { + activeSockets.put(conn, new WebsocketEventHandler(conn, dependabilityMng)); + } + // Parse the request - Request req = parseRequest(message, conn); + InternalRequest req = parseRequest(message, conn); if (req == null) { - logger.error("Failed to parse message: "+req); + logger.error("Failed to parse message: " + req); + activeSockets.remove(conn); return; } - - // Add active socket - if (!activeSockets.containsKey(conn)) { - activeSockets.put(conn, new WebsocketEventHandler(conn, jmx, dependabilityMng)); - } - activeSockets.get(conn).startTiming(); Timings.log(req); // Schedule the request - scheduler.schedule(req, activeSockets.get(conn)); + ScheduledRequest request = scheduler.schedule(req, activeSockets.get(conn)); + if (request == null) { + logger.error("Out of tokens"); + ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_ACCEPTABLE, + "Too many pending requests"); + conn.send(response.toString()); + } } /** @@ -164,7 +169,7 @@ public void onMessage(WebSocket conn, String message) { }} * */ - protected Request parseRequest(String request, WebSocket conn) + protected InternalRequest parseRequest(String request, WebSocket conn) throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException { JsonObject req; ErrorResponse error; @@ -206,7 +211,7 @@ protected Request parseRequest(String request, WebSocket conn) } catch (Exception e) { } - return new SubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri, null); + return new InternalSubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri,activeSockets.get(conn)); } else if (req.has("unsubscribe")) { String spuid; try { @@ -216,10 +221,10 @@ protected Request parseRequest(String request, WebSocket conn) conn.send(error.toString()); return null; } - - return new UnsubscribeRequest(spuid); + + return new InternalUnsubscribeRequest(spuid); } - + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Bad request: " + request); conn.send(error.toString()); return null; @@ -248,7 +253,7 @@ public void onFragment(WebSocket conn, Framedata fragment) { logger.debug("Fragmented message: " + fragmentedMessages.get(conn)); if (fragment.isFin()) { - jmx.onFragmentedMessage(); + WebsocketBeans.onFragmentedMessage(); onMessage(conn, fragmentedMessages.get(conn)); fragmentedMessages.put(conn, null); @@ -267,7 +272,7 @@ public void onError(WebSocket conn, Exception ex) { if (!conn.getResourceDescriptor().equals(path)) return; - jmx.onError(); + WebsocketBeans.onError(); } @Override @@ -281,21 +286,21 @@ public void onStart() { @Override public void reset() { - jmx.reset(); + WebsocketBeans.reset(); } @Override public long getMessages() { - return jmx.getMessages(); + return WebsocketBeans.getMessages(); } @Override public long getFragmented() { - return jmx.getFragmented(); + return WebsocketBeans.getFragmented(); } @Override public long getErrors() { - return jmx.getErrors(); + return WebsocketBeans.getErrors(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java new file mode 100644 index 00000000..2cf86e5e --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java @@ -0,0 +1,19 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +public class InternalQueryRequest extends InternalUQRequest { + + public InternalQueryRequest(String sparql, String defaultGraphUri, String namedGraphUri) { + super(sparql, defaultGraphUri, namedGraphUri); + } + + @Override + public String toString() { + return "*QUERY* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof InternalQueryRequest)) return false; + return sparql.equals(((InternalQueryRequest)obj).sparql); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java new file mode 100644 index 00000000..afbbf62f --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java @@ -0,0 +1,20 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +public abstract class InternalRequest { + + public boolean isQueryRequest() { + return this.getClass().equals(InternalQueryRequest.class); + } + + public boolean isUpdateRequest() { + return this.getClass().equals(InternalUpdateRequest.class); + } + + public boolean isSubscribeRequest() { + return this.getClass().equals(InternalSubscribeRequest.class); + } + + public boolean isUnsubscribeRequest() { + return this.getClass().equals(InternalUnsubscribeRequest.class); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java new file mode 100644 index 00000000..aa256265 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java @@ -0,0 +1,39 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +import it.unibo.arces.wot.sepa.engine.core.EventHandler; + +public class InternalSubscribeRequest extends InternalQueryRequest { + + private String alias = null; + private EventHandler handler; + + public InternalSubscribeRequest(String sparql, String alias,String defaultGraphUri, String namedGraphUri,EventHandler handler) { + super(sparql, defaultGraphUri, namedGraphUri); + + this.alias = alias; + this.handler = handler; + } + + @Override + public String toString() { + return "*SUBSCRIBE* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; + } + + public String getSpuid() { + return sparql; + } + + public String getAlias() { + return alias; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof InternalSubscribeRequest)) return false; + return sparql.equals(((InternalSubscribeRequest)obj).sparql); + } + + public EventHandler getEventHandler() { + return handler; + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java new file mode 100644 index 00000000..1d060daa --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java @@ -0,0 +1,30 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +public abstract class InternalUQRequest extends InternalRequest { + protected String sparql; + protected String defaultGraphUri; + protected String namedGraphUri; + + public InternalUQRequest(String sparql,String defaultGraphUri,String namedGraphUri) { + this.sparql = sparql; + this.defaultGraphUri = defaultGraphUri; + this.namedGraphUri = namedGraphUri; + } + + public String getSparql() { + return sparql; + } + + public String getDefaultGraphUri() { + return defaultGraphUri; + } + + public String getNamedGraphUri() { + return namedGraphUri; + } + + @Override + public int hashCode() { + return sparql.hashCode(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java new file mode 100644 index 00000000..9f5bd32e --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java @@ -0,0 +1,24 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +public class InternalUnsubscribeRequest extends InternalRequest { + protected String spuid; + + public InternalUnsubscribeRequest(String spuid) { + this.spuid = spuid; + } + + @Override + public String toString() { + return "*UNSUBSCRIBE* "+spuid; + } + + public String getSpuid() { + return spuid; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof InternalUnsubscribeRequest)) return false; + return spuid.equals(((InternalUnsubscribeRequest)obj).spuid); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java new file mode 100644 index 00000000..bf169200 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java @@ -0,0 +1,19 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +public class InternalUpdateRequest extends InternalUQRequest { + + public InternalUpdateRequest(String sparql, String defaultGraphUri, String namedGraphUri) { + super(sparql, defaultGraphUri, namedGraphUri); + } + + @Override + public String toString() { + return "*UPDATE* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof InternalUpdateRequest)) return false; + return sparql.equals(((InternalUpdateRequest)obj).sparql); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledRequest.java index 21822950..b08909a2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledRequest.java @@ -1,23 +1,32 @@ package it.unibo.arces.wot.sepa.engine.scheduling; -import it.unibo.arces.wot.sepa.commons.request.Request; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; public class ScheduledRequest { - private Request request = null; + private InternalRequest request = null; private ResponseHandler handler = null; + private int token; - public ScheduledRequest(int token,Request request,ResponseHandler handler) { + public ScheduledRequest(int token,InternalRequest request,ResponseHandler handler) { this.request = request; - this.request.setToken(token); this.handler = handler; + this.token = token; } - public Request getRequest() { + @Override + public String toString() { + return "REQUEST #"+token+" : "+request.toString(); + } + + public int getToken() { + return token; + } + + public InternalRequest getRequest() { return request; } - public ResponseHandler getHandler() { + public ResponseHandler getResponseHandler() { return handler; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledResponse.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledResponse.java new file mode 100644 index 00000000..2b65ba14 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/ScheduledResponse.java @@ -0,0 +1,26 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +import it.unibo.arces.wot.sepa.commons.response.Response; + +public class ScheduledResponse { + private int token = -1; + private Response response = null; + + public ScheduledResponse(int token,Response response) { + this.token = token; + this.response = response; + } + + public Response getResponse() { + return response; + } + + public int getToken() { + return token; + } + + @Override + public String toString() { + return "RESPONSE #"+token+" : "+response.toString(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 0bc6145a..d3132407 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -18,23 +18,20 @@ package it.unibo.arces.wot.sepa.engine.scheduling; -import java.io.IOException; import java.util.HashMap; -import java.util.Vector; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; -import it.unibo.arces.wot.sepa.commons.request.Request; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.SchedulerBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.timing.Timings; +import it.unibo.arces.wot.sepa.engine.timing.Timings; /** * This class represents the scheduler of the SPARQL Event Processing Engine @@ -43,121 +40,86 @@ public class Scheduler extends Thread implements SchedulerMBean { private static final Logger logger = LogManager.getLogger(); - // Request tokens - private Vector tokens = new Vector(); - + private final AtomicBoolean running = new AtomicBoolean(true); + // Responders private HashMap responders = new HashMap(); - // Synchronized queue - private SchedulerRequestResponseQueue queue; + // Synchronized queues + private final SchedulerQueue queue; - public Scheduler(EngineProperties properties,SchedulerRequestResponseQueue queue) { + public Scheduler(EngineProperties properties) { if (properties == null) { logger.error("Properties are null"); throw new IllegalArgumentException("Properties are null"); } - if (queue == null) { - logger.error("Queue is null"); - throw new IllegalArgumentException("Queue is null"); - } - this.queue = queue; - // Initialize token jar - for (int i = 0; i < properties.getSchedulingQueueSize(); i++) - tokens.addElement(i); + queue = new SchedulerQueue(properties.getSchedulingQueueSize()); // JMX SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SchedulerBeans.setQueueSize(properties.getSchedulingQueueSize()); - this.setName("SEPA-Scheduler"); + setName("SEPA-Scheduler"); } - public synchronized void schedule(Request request, ResponseHandler handler) { - int token = getToken(); - if (token == -1) { + public synchronized ScheduledRequest schedule(InternalRequest request, ResponseHandler handler) { + if (request == null || handler == null) { + logger.error("Request handler or request are null"); + return null; + } + + // Add request to the scheduler queue (null means no more tokens) + ScheduledRequest scheduled = queue.addRequest(request, handler); + + // No more tokens + if (scheduled == null) { SchedulerBeans.newRequest(request, false); - try { - logger.error("Request refused: too many pending requests: "+request); - if (handler != null) handler.sendResponse(new ErrorResponse(-1, 500, "Request refused: too many pending requests")); - } catch (IOException e) { - logger.error("Failed to send response on out of tokens"); - } - return; + logger.error("Request refused: too many pending requests: "+request); + return null; } - - // Responder - responders.put(token, handler); - - queue.addRequest(new ScheduledRequest(token, request, handler)); + + logger.debug(scheduled); + + // Register response handler + responders.put(scheduled.getToken(), handler); Timings.log(request); SchedulerBeans.newRequest(request, true); - } - - /** - * Returns a new token if more tokens are available or -1 otherwise - * - * @return an int representing the token - */ - private synchronized int getToken() { - if (tokens.size() == 0) { - logger.error("No tokens available"); - return -1; - } - - Integer token = tokens.get(0); - tokens.removeElementAt(0); - - logger.trace("Get token #" + token + " (Available: " + tokens.size() + ")"); - - SchedulerBeans.tokenLeft(tokens.size()); - - return token; - } - - /** - * Release an used token - * - * @return true if success, false if the token to be released has not been - * acquired - */ - private synchronized void releaseToken(Integer token) { - if (token == -1) - return; - - if (tokens.contains(token)) { - logger.warn("Request to release a unused token: " + token + " (Available tokens: " + tokens.size() + ")"); - } else { - tokens.insertElementAt(token, tokens.size()); - logger.trace("Release token #" + token + " (Available: " + tokens.size() + ")"); - - SchedulerBeans.tokenLeft(tokens.size()); - } + + return scheduled; } @Override public void run() { - while(true) { - Response response; + while(running.get()) { try { - response = queue.waitResponse(); + // Wait for response + ScheduledResponse response = queue.waitResponse(); + + logger.debug(response); + + // The token + int token = response.getToken(); + + // Send response back and remove handler + if (responders.get(token) != null) + try { + responders.get(token).sendResponse(response.getResponse()); + } catch (SEPAProtocolException e) { + logger.error("Failed to send response: "+e.getMessage()); + } + responders.remove(token); } catch (InterruptedException e) { - return; + running.set(false); } - try { - if (responders.get(response.getToken()) != null) responders.get(response.getToken()).sendResponse(response); - } catch (IOException e) { - logger.error("Failed to send response: " + e.getMessage()); - } - responders.remove(response.getToken()); - - // RELEASE TOKEN - releaseToken(response.getToken()); } } + + public void finish() { + running.set(false); + } public String getStatistics() { return SchedulerBeans.getStatistics(); @@ -193,4 +155,8 @@ public long getRequests_scheduled() { public int getQueueSize() { return SchedulerBeans.getQueueSize(); } + + public SchedulerQueue getSchedulerQueue() { + return queue; + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java new file mode 100644 index 00000000..69230998 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java @@ -0,0 +1,119 @@ +package it.unibo.arces.wot.sepa.engine.scheduling; + +import java.util.Vector; +import java.util.concurrent.LinkedBlockingQueue; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.bean.SchedulerBeans; +import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; + +public class SchedulerQueue { + private static final Logger logger = LogManager.getLogger(); + + // Tokens + private final Vector tokens = new Vector(); + + // Requests + private final LinkedBlockingQueue updates = new LinkedBlockingQueue(); + private final LinkedBlockingQueue queries = new LinkedBlockingQueue(); + private final LinkedBlockingQueue subscribesUnsubscribes = new LinkedBlockingQueue(); + + // Broken subscriptions + private final LinkedBlockingQueue toBeKilled = new LinkedBlockingQueue(); + + // Responses + private final LinkedBlockingQueue responses = new LinkedBlockingQueue(); + + public SchedulerQueue(long size) { + // Initialize token jar + for (int i = 0; i < size; i++) + tokens.addElement(i); + } + + /** + * Returns a new token if more tokens are available or -1 otherwise + * + * @return an int representing the token + */ + private synchronized int getToken() { + if (tokens.size() == 0) { + logger.error("No tokens available"); + return -1; + } + + Integer token = tokens.get(0); + tokens.removeElementAt(0); + + logger.trace("Get token #" + token + " (Available: " + tokens.size() + ")"); + + SchedulerBeans.tokenLeft(tokens.size()); + + return token; + } + + /** + * Release an used token + * + * @return true if success, false if the token to be released has not been + * acquired + */ + private synchronized void releaseToken(Integer token) { + if (token == -1) + return; + + if (tokens.contains(token)) { + logger.warn("Request to release a unused token: " + token + " (Available tokens: " + tokens.size() + ")"); + } else { + tokens.insertElementAt(token, tokens.size()); + logger.trace("Release token #" + token + " (Available: " + tokens.size() + ")"); + + SchedulerBeans.tokenLeft(tokens.size()); + } + } + + public synchronized ScheduledRequest addRequest(InternalRequest req,ResponseHandler handler) { + int token = getToken(); + if (token == -1) return null; + + ScheduledRequest request = new ScheduledRequest(token,req,handler); + if (req.isUpdateRequest()) updates.add(request); + else if (req.isQueryRequest()) queries.add(request); + else if (req.isSubscribeRequest()) subscribesUnsubscribes.add(request); + else if (req.isUnsubscribeRequest())subscribesUnsubscribes.add(request); + + return request; + } + + public ScheduledRequest waitUpdateRequest() throws InterruptedException { + return updates.take(); + } + + public ScheduledRequest waitQueryRequest() throws InterruptedException { + return queries.take(); + } + + public ScheduledRequest waitSubscribeUnsubscribeRequest() throws InterruptedException { + return subscribesUnsubscribes.take(); + } + + public synchronized void addResponse(int token,Response res) { + responses.offer(new ScheduledResponse(token,res)); + } + + public ScheduledResponse waitResponse() throws InterruptedException { + ScheduledResponse ret = responses.take(); + releaseToken(ret.getToken()); + return ret; + } + + public synchronized void killSpuid(String spuid) { + toBeKilled.offer(spuid); + } + + public String waitSpuid2Kill() throws InterruptedException { + return toBeKilled.take(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java deleted file mode 100644 index a8cab2db..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerRequestResponseQueue.java +++ /dev/null @@ -1,26 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.scheduling; - -import java.util.concurrent.LinkedBlockingQueue; - -import it.unibo.arces.wot.sepa.commons.response.Response; - -public class SchedulerRequestResponseQueue { - private LinkedBlockingQueue requests = new LinkedBlockingQueue(); - private LinkedBlockingQueue responses = new LinkedBlockingQueue(); - - public void addRequest(ScheduledRequest req) { - requests.offer(req); - } - - public ScheduledRequest waitRequest() throws InterruptedException { - return requests.take(); - } - - public void addResponse(Response res) { - responses.offer(res); - } - - public Response waitResponse() throws InterruptedException { - return responses.take(); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java new file mode 100644 index 00000000..87a6f747 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java @@ -0,0 +1,49 @@ +package it.unibo.arces.wot.sepa.engine.timing; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; + +public class Timings { + private static final Logger logger = LogManager.getLogger(); + + public static long getTime() { + return System.nanoTime(); + } + + public synchronized static void log(String tag,long start,long stop) { + String message = String.format("%d,%s,%d",System.currentTimeMillis(),tag,stop-start); + logger.log(Level.getLevel("timing"),message); + } + + public synchronized static void log(InternalRequest request) { + long start = getTime(); + + String tag; + if (request.isUpdateRequest()) tag = "UPDATE_REQUEST"; + else if (request.isSubscribeRequest()) tag = "SUBSCRIBE_REQUEST"; + else if(request.isQueryRequest()) tag = "QUERY_REQUEST"; + else if(request.isUnsubscribeRequest()) tag = "UNSUBSCRIBE_REQUEST"; + else tag = "UNKNOWN_REQUEST"; + + + log(tag,start,start); + } + + public synchronized static void log(Response response) { + long start = getTime(); + + String tag; + if (response.isUpdateResponse()) tag = "UPDATE_RESPONSE"; + else if (response.isSubscribeResponse()) tag = "SUBSCRIBE_RESPONSE"; + else if(response.isQueryResponse()) tag = "QUERY_RESPONSE"; + else if(response.isUnsubscribeResponse()) tag = "UNSUBSCRIBE_RESPONSE"; + else if(response.isError()) tag = "ERROR_RESPONSE"; + else tag = "UNKNOWN_RESPONSE"; + + log(tag,start,start); + } +} diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index 626b507e..be72e020 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -26,8 +26,8 @@ ALL Integer.MAX_VALUE - - + + diff --git a/engine/src/test/java/engine/QueryProcessorTest.java b/engine/src/test/java/engine/QueryProcessorTest.java deleted file mode 100644 index b9afd970..00000000 --- a/engine/src/test/java/engine/QueryProcessorTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package engine; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.engine.processing.QueryProcessor; - -public class QueryProcessorTest { - private static QueryProcessor processor; - - public static void main(String[] args) throws SEPAProtocolException, SEPAPropertiesException{ - SPARQL11Properties properties = new SPARQL11Properties("endpoint.jpar"); - - System.out.println(properties.toString()); - - processor = new QueryProcessor(properties,null); - - while(true) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - System.out.println(processor.process(new QueryRequest("select ?s ?p ?o where {?s ?p ?o}"))); - } - - } -} diff --git a/engine/src/test/java/engine/SchedulerTest.java b/engine/src/test/java/engine/SchedulerTest.java deleted file mode 100644 index dde62db0..00000000 --- a/engine/src/test/java/engine/SchedulerTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package engine; - -import java.io.IOException; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.core.EngineProperties; -import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; - -public class SchedulerTest { - private static Scheduler scheduler; - - public class Handler implements ResponseHandler { - - @Override - public void sendResponse(Response response) throws IOException { - System.out.println(response); - - } - } - - public static void main(String[] args) throws SEPAPropertiesException{ - EngineProperties properties = new EngineProperties("engine.jpar"); - - System.out.println(properties.toString()); - - scheduler = new Scheduler(properties, null); - Handler handler = new SchedulerTest().new Handler(); - - while(true) { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - UpdateRequest update = new UpdateRequest("PREFIX test: delete {?s ?p ?o} insert {test:s test:p \""+Math.random()+"\"} where {OPTIONAL{?s ?p ?o}}"); - scheduler.schedule(update,handler); - - QueryRequest query = new QueryRequest("select * where {?s ?p ?o}"); - scheduler.schedule(query,handler); - - } - - } - - -} diff --git a/engine/src/test/java/engine/UpdateProcessorTest.java b/engine/src/test/java/engine/UpdateProcessorTest.java deleted file mode 100644 index dcbed536..00000000 --- a/engine/src/test/java/engine/UpdateProcessorTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package engine; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.engine.processing.UpdateProcessor; - -public class UpdateProcessorTest { - private static UpdateProcessor processor; - - public static void main(String[] args) throws SEPAPropertiesException, SEPAProtocolException { - SPARQL11Properties properties = new SPARQL11Properties("endpoint.jpar"); - - System.out.println(properties.toString()); - - processor = new UpdateProcessor(properties,null); - - while(true) { - try { - Thread.sleep(500); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - System.out.println(processor.process(new UpdateRequest("PREFIX test: delete {?s ?p ?o} insert {test:s test:p \""+Math.random()+"\"} where {?s ?p ?o}"))); - } - - } -} diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java deleted file mode 100644 index a08f206c..00000000 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscription; - -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISPU; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -public class SPUMangerTest { - - private SPUManager spuManger; - - @Before - public void Init(){ - spuManger = new SPUManager(); - } - - @Test - public void registerTest(){ - FakeSPU spu = new FakeSPU("Test"); - spuManger.register(spu,null); - Assert.assertEquals("SPU not registered",spuManger.size(),1); - } - - @Test - public void unRegisterTest(){ - FakeSPU spu = new FakeSPU("123Test"); - spuManger.register(spu,null); - Assert.assertEquals("SPU not registered",spuManger.size(),1); - spuManger.unRegister("123Test"); - Assert.assertEquals("SPU not succesfully deleted",spuManger.size(),0); - } - - @Test(expected = IllegalArgumentException.class) - public void unRegisterInvalidID(){ - spuManger.unRegister("pluto"); - } -// -// @Test -// public void isValidUIDTest(){ -// Assert.assertFalse(spuManger.isValidSpuId("Cap")); -// spuManger.register(new FakeSPU("Ironman"),null); -// Assert.assertTrue(spuManger.isValidSpuId("Ironman")); -// } - - private class FakeSPU implements ISPU{ - - private String uid; - - public FakeSPU(String uid){ - - this.uid = uid; - } - @Override - public Response init() { - return null; - } - - @Override - public BindingsResults getLastBindings() { - return null; - } - - @Override - public void terminate() { - - } - - @Override - public String getUUID() { - return uid; - } - - @Override - public void process(UpdateResponse res) { - - } - - @Override - public void run() { - - } - } -} - - diff --git a/engine/src/test/resources/endpoint.jpar b/engine/src/test/resources/endpoint.jpar new file mode 100644 index 00000000..8af1db1d --- /dev/null +++ b/engine/src/test/resources/endpoint.jpar @@ -0,0 +1,33 @@ +{ + "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8890, + "query": { + "path": "/sparql", + "method": "URL_ENCODED_POST", + "format": "JSON" + }, + "update": { + "path": "/sparql", + "method": "POST", + "format": "JSON" + } + }, + "graphs": { + "default-graph-uri": "http://default/", + "named-graph-uri": "http://default/", + "using-graph-uri": "http://default/", + "using-named-graph-uri": "http://default/" + } +} \ No newline at end of file diff --git a/engine/src/test/resources/engine-secure.jpar b/engine/src/test/resources/engine-secure.jpar new file mode 100644 index 00000000..9463d40b --- /dev/null +++ b/engine/src/test/resources/engine-secure.jpar @@ -0,0 +1,33 @@ +{ + "parameters": { + "scheduler": { + "queueSize": 100 + }, + "processor": { + "updateTimeout": 60000, + "queryTimeout": 60000, + "maxConcurrentRequests": 5, + "reliableUpdate" : true + }, + "spu": { + "timeout": 5000 + }, + "gates": { + "secure": true, + "paths": { + "securePath": "/secure", + "update": "/update", + "query": "/query", + "subscribe": "/subscribe", + "register": "/oauth/register", + "tokenRequest": "/oauth/token" + }, + "ports": { + "http": 8000, + "https": 8443, + "ws": 9000, + "wss": 9443 + } + } + } +} \ No newline at end of file diff --git a/engine/src/test/resources/engine.jpar b/engine/src/test/resources/engine.jpar new file mode 100644 index 00000000..c9beca29 --- /dev/null +++ b/engine/src/test/resources/engine.jpar @@ -0,0 +1,33 @@ +{ + "parameters": { + "scheduler": { + "queueSize": 100 + }, + "processor": { + "updateTimeout": 60000, + "queryTimeout": 60000, + "maxConcurrentRequests": 5, + "reliableUpdate" : true + }, + "spu": { + "timeout": 5000 + }, + "gates": { + "secure": false, + "paths": { + "securePath": "/secure", + "update": "/update", + "query": "/query", + "subscribe": "/subscribe", + "register": "/oauth/register", + "tokenRequest": "/oauth/token" + }, + "ports": { + "http": 8000, + "https": 8443, + "ws": 9000, + "wss": 9443 + } + } + } +} \ No newline at end of file diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java index 198a968b..b15af8dd 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicClient.java @@ -1,5 +1,7 @@ package it.unibo.arces.wot.sepa.apps.chat; +import java.io.IOException; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,14 +32,21 @@ public BasicClient(String userURI, Users users, int messages) @Override public void run() { - while (!joinChat()) { + do { + logger.info("Joining the chat..."); try { - logger.info("Joining the chat..."); - Thread.sleep(1000); - } catch (InterruptedException e) { - return; + joinChat(); + } catch (SEPASecurityException | IOException | SEPAPropertiesException | SEPAProtocolException e) { + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + return; + } + continue; } - } + break; + } while(true); + logger.info("Chat joined!"); for (int i = 0; i < messages; i++) { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java index b7c9f070..feec06bb 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/BasicHandler.java @@ -12,18 +12,29 @@ public class BasicHandler implements ISubscriptionHandler { @Override public void onSemanticEvent(Notification notify) { - logger.info("Notify: "+notify); + logger.info("onSemanticEvent: "+notify); } @Override public void onBrokenConnection() { - logger.info("Broken socket"); + logger.info("onBrokenConnection"); } @Override public void onError(ErrorResponse errorResponse) { - logger.info("Error: "+errorResponse); + logger.info("onError: "+errorResponse); + } + + @Override + public void onSubscribe(String spuid, String alias) { + logger.info("onSubscribe: "+spuid + " Alias: "+alias); + + } + + @Override + public void onUnsubscribe(String spuid) { + logger.info("onUnsubscribe: "+spuid); } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java index 81bbcfc1..aa09bb8b 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/ChatClient.java @@ -1,5 +1,7 @@ package it.unibo.arces.wot.sepa.apps.chat; +import java.io.IOException; + import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -18,16 +20,14 @@ public ChatClient(String userURI) throws SEPAProtocolException, SEPASecurityExce remover = new Remover(userURI,this); } - public boolean joinChat() { - if (!remover.joinChat()) return false; - if (!receiver.joinChat()) return false; - return true; + public void joinChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + remover.joinChat(); + receiver.joinChat(); } - public boolean leaveChat() { - if (!remover.leaveChat()) return false; - if (!receiver.leaveChat()) return false; - return true; + public void leaveChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + remover.leaveChat(); + receiver.leaveChat(); } public boolean sendMessage(String receiverURI,String message) { diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java index 45fd323b..8f53317e 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Receiver.java @@ -9,8 +9,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; @@ -34,39 +32,12 @@ public Receiver(String receiverURI,ChatClient client) this.client = client; } - public boolean joinChat() { - if (joined) - return true; - - Response ret; - try { - ret = subscribe(); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - return false; - } - joined = !ret.isError(); - - if (joined) - onAddedResults(((SubscribeResponse) ret).getBindingsResults()); - - return joined; + public void joinChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + if (!joined) subscribe(5000); } - public boolean leaveChat() { - if (!joined) - return true; - - Response ret; - try { - ret = unsubscribe(); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - return false; - } - joined = !ret.isUnsubscribeResponse(); - - return !joined; + public void leaveChat() throws SEPAProtocolException, SEPASecurityException, IOException, SEPAPropertiesException { + if (joined) unsubscribe(5000); } @Override @@ -104,7 +75,12 @@ public void onRemovedResults(BindingsResults results) { public void onBrokenConnection() { joined = false; - while (!joinChat()) { + while (!joined) { + try { + joinChat(); + } catch (SEPASecurityException | IOException | SEPAPropertiesException | SEPAProtocolException e1) { + + } try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -116,4 +92,15 @@ public void onBrokenConnection() { @Override public void onError(ErrorResponse errorResponse) { } + + @Override + public void onSubscribe(String spuid, String alias) { + joined = true; + + } + + @Override + public void onUnsubscribe(String spuid) { + joined = false; + } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java index 1384a326..e39ba293 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Remover.java @@ -9,8 +9,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; @@ -29,39 +27,12 @@ public Remover(String senderURI,ChatClient client) throws SEPAProtocolException, this.setSubscribeBindingValue("sender", new RDFTermURI(senderURI)); } - public boolean joinChat() { - if (joined) - return true; - - Response ret; - try { - ret = subscribe(); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - return false; - } - joined = !ret.isError(); - - if (joined) - onAddedResults(((SubscribeResponse) ret).getBindingsResults()); - - return joined; + public void joinChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + if (!joined) subscribe(5000); } - public boolean leaveChat() { - if (!joined) - return true; - - Response ret; - try { - ret = unsubscribe(); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - return false; - } - joined = !ret.isUnsubscribeResponse(); - - return !joined; + public void leaveChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + if (joined) unsubscribe(5000); } @Override @@ -93,7 +64,12 @@ public void onRemovedResults(BindingsResults results) { public void onBrokenConnection() { joined = false; - while (!joinChat()) { + while (!joined) { + try { + joinChat(); + } catch (SEPASecurityException | IOException | SEPAPropertiesException | SEPAProtocolException e1) { + + } try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -107,4 +83,13 @@ public void onError(ErrorResponse errorResponse) { } + @Override + public void onSubscribe(String spuid, String alias) { + joined = true; + } + + @Override + public void onUnsubscribe(String spuid) { + joined = false; + } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java index da5f30f8..6df00b1a 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/UpdateQueryTest.java @@ -179,7 +179,7 @@ public List sent(String receiver) { Response ret; try { ret = this.query(SENT, bindings,5000); - } catch (SEPAProtocolException | SEPASecurityException | IOException | SEPAPropertiesException e) { + } catch (SEPAProtocolException | SEPASecurityException | SEPAPropertiesException e) { logger.error(e.getMessage()); return list; } @@ -207,7 +207,7 @@ public List received(String sender) { Response ret; try { ret = this.query(RECEIVED, bindings,5000); - } catch (SEPAProtocolException | SEPASecurityException | IOException | SEPAPropertiesException e) { + } catch (SEPAProtocolException | SEPASecurityException | SEPAPropertiesException e) { logger.error(e.getMessage()); return list; } @@ -231,7 +231,7 @@ public List users() { Response ret; try { ret = this.query(USERS, null,5000); - } catch (SEPAProtocolException | SEPASecurityException | IOException | SEPAPropertiesException e) { + } catch (SEPAProtocolException | SEPASecurityException | SEPAPropertiesException e) { logger.error(e.getMessage()); return list; } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java index 6dc190ab..0810d7f4 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/Users.java @@ -11,8 +11,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; @@ -29,39 +27,12 @@ public Users() throws SEPAProtocolException, SEPAPropertiesException, SEPASecuri super(new JSAP("chat.jsap"), "USERS"); } - public boolean joinChat() { - if (joined) - return true; - - Response ret; - try { - ret = subscribe(); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - return false; - } - joined = !ret.isError(); - - if (joined) - onAddedResults(((SubscribeResponse) ret).getBindingsResults()); - - return joined; + public void joinChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + if (!joined) subscribe(5000); } - public boolean leaveChat() { - if (!joined) - return true; - - Response ret; - try { - ret = unsubscribe(); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - return false; - } - joined = !ret.isUnsubscribeResponse(); - - return !joined; + public void leaveChat() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException { + if (joined) unsubscribe(5000); } public Set getUsers() { @@ -103,7 +74,12 @@ public void onRemovedResults(BindingsResults results) { public void onBrokenConnection() { joined = false; - while (!joinChat()) { + while (!joined) { + try { + joinChat(); + } catch (SEPASecurityException | IOException | SEPAPropertiesException | SEPAProtocolException e1) { + + } try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -114,7 +90,17 @@ public void onBrokenConnection() { @Override public void onError(ErrorResponse errorResponse) { + logger.error(errorResponse); + } + @Override + public void onSubscribe(String spuid, String alias) { + joined = true; + } + + @Override + public void onUnsubscribe(String spuid) { + joined = false; } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java index d53be2e3..2665b637 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMonitor.java @@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.eclipse.paho.client.mqttv3.MqttException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; @@ -19,7 +20,7 @@ public class MQTTMonitor { private static MQTTMapper mqttInitializer; public static void main(String[] args) - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException { + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException, MqttException { if (args.length != 1) { logger.error("Please provide the jsap file as argument"); System.exit(-1); @@ -27,7 +28,7 @@ public static void main(String[] args) // Logger ObservationLogger analytics = new ObservationLogger(args[0]); - analytics.subscribe(); + analytics.subscribe(5000); // Remover ObservationRemover remover = new ObservationRemover(args[0]); @@ -41,19 +42,19 @@ public static void main(String[] args) // Create MQTT smartifier smartifier = new MQTTSmartifier(args[0]); - if (smartifier.start()) { - logger.info("Press any key to exit..."); - try { - System.in.read(); - } catch (IOException e) { - logger.warn(e.getMessage()); - } - - logger.info("Stop MQTT smartifier"); - smartifier.stop(); - - logger.info("Stopped"); + smartifier.start(); + + logger.info("Press any key to exit..."); + try { + System.in.read(); + } catch (IOException e) { + logger.warn(e.getMessage()); } + + logger.info("Stop MQTT smartifier"); + smartifier.stop(); + + logger.info("Stopped"); analytics.close(); diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java index b845e26b..62bcb846 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java @@ -24,8 +24,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; @@ -176,17 +174,7 @@ else if (appProfile.getExtendedData().get("jsonTopics").getAsJsonObject().get(to } } - public boolean start() throws SEPASecurityException, IOException, SEPAPropertiesException { - // Subscribe to observation-topic mapping - Response ret = subscribe(); - - if (ret.isError()) { - logger.fatal("Failed to subscribe: " + ret); - return false; - } - SubscribeResponse results = (SubscribeResponse) ret; - onAddedResults(results.getBindingsResults()); - + public void start() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException, MqttException { if (getApplicationProfile().getExtendedData().get("simulate").getAsBoolean()) simulator(); else { @@ -220,13 +208,9 @@ public boolean start() throws SEPASecurityException, IOException, SEPAProperties String clientID = MqttClient.generateClientId(); logger.info("Client ID: " + clientID); logger.info("Server URI: " + serverURI); - try { - mqttClient = new MqttClient(serverURI, clientID); - } catch (MqttException e) { - logger.error(e.getMessage()); - return false; - } - + + mqttClient = new MqttClient(serverURI, clientID); + // Connect logger.info("Connecting..."); MqttConnectOptions options = new MqttConnectOptions(); @@ -245,18 +229,15 @@ public boolean start() throws SEPASecurityException, IOException, SEPAProperties // Subscribe mqttClient.setCallback(this); logger.info("Subscribing..."); - try { - mqttClient.subscribe(topicsFilter); - } catch (MqttException e) { - logger.error(e.getMessage()); - return false; - } - + + mqttClient.subscribe(topicsFilter); + for(String topic:topicsFilter) logger.info("MQTT client " + clientID + " subscribed to " + serverURI + " Topic filter " + topic); // MQTT: end } - - return true; + + // Subscribe to observation-topic mapping + subscribe(5000); } public void simulator() { @@ -386,4 +367,16 @@ public void onError(ErrorResponse errorResponse) { // TODO Auto-generated method stub } + + @Override + public void onSubscribe(String spuid, String alias) { + // TODO Auto-generated method stub + + } + + @Override + public void onUnsubscribe(String spuid) { + // TODO Auto-generated method stub + + } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java index 5a121d51..6e29e3d6 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java @@ -52,4 +52,16 @@ public void onBrokenConnection() {} @Override public void onError(ErrorResponse errorResponse) {} + + @Override + public void onSubscribe(String spuid, String alias) { + // TODO Auto-generated method stub + + } + + @Override + public void onUnsubscribe(String spuid) { + // TODO Auto-generated method stub + + } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java index 3a444396..f77b6b39 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationSimulator.java @@ -19,7 +19,7 @@ public class ObservationSimulator extends Producer implements Runnable { private int value = 15; private long timeout = 2000; - public ObservationSimulator(JSAP appProfile,String observation,String update) throws SEPAProtocolException, SEPASecurityException { + public ObservationSimulator(JSAP appProfile,String observation,String update) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(appProfile, update); this.setUpdateBindingValue("observation", new RDFTermURI(observation)); diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index e25b2174..18305397 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -88,7 +88,6 @@ import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.commons.sparql.ARBindingsResults; @@ -142,7 +141,7 @@ public class Dashboard { private SortedListModel updateListDM = new SortedListModel(); private SortedListModel queryListDM = new SortedListModel(); - private JTabbedPane subscriptions; + private HashMap subscriptions = new HashMap(); private HashMap subscriptionResultsDM = new HashMap(); private HashMap subscriptionResultsLabels = new HashMap(); private HashMap subscriptionResultsTables = new HashMap(); @@ -195,6 +194,8 @@ public class Dashboard { private SEPASecurityManager sm; + private JTabbedPane subscriptionsPanel = new JTabbedPane(JTabbedPane.TOP); + class DashboardHandler implements ISubscriptionHandler { @Override public void onSemanticEvent(Notification n) { @@ -229,6 +230,80 @@ public void onBrokenConnection() { public void onError(ErrorResponse errorResponse) { logger.error(errorResponse.getErrorMessage()); } + + @Override + public void onSubscribe(String spuid, String alias) { + // Subscription panel + JPanel sub = new JPanel(); + + // Layout + GridBagConstraints layoutFill = new GridBagConstraints(); + layoutFill.fill = GridBagConstraints.BOTH; + sub.setLayout(new BoxLayout(sub, BoxLayout.Y_AXIS)); + sub.setName(queryList.getSelectedValue()); + + // Query label + JLabel queryLabel = new JLabel( + "" + querySPARQL.getText() + ""); + queryLabel.setFont(new Font("Arial", Font.BOLD, 14)); + + // Info label + JLabel info = new JLabel("Info"); + info.setText(spuid); + subscriptionResultsLabels.put(spuid, info); + + // Unsubscribe button + JButton unsubscribeButton = new JButton(spuid); + unsubscribeButton.setEnabled(true); + unsubscribeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + sepaClient.unsubscribe(spuid,Integer.parseInt(timeout.getText())); + } catch (NumberFormatException | SEPASecurityException | SEPAPropertiesException + | SEPAProtocolException e1) { + logger.error(e1.getMessage()); + } + } + }); + + // Results table + subscriptionResultsDM.put(spuid, new BindingsTableModel()); + + JTable bindingsResultsTable = new JTable(subscriptionResultsDM.get(spuid)); + JScrollPane bindingsResults = new JScrollPane(); + bindingsResults.setViewportView(bindingsResultsTable); + + bindingsResultsTable.setDefaultRenderer(Object.class, bindingsRender); + bindingsResultsTable.setAutoCreateRowSorter(true); + bindingsResultsTable.registerKeyboardAction(new CopyAction(), + KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + JComponent.WHEN_FOCUSED); + bindingsResultsTable.setCellSelectionEnabled(true); + + subscriptionResultsTables.put(spuid, bindingsResultsTable); + + // Add all elements + sub.add(queryLabel); + sub.add(unsubscribeButton); + sub.add(bindingsResults); + sub.add(info); + + // Add tab + subscriptionsPanel.add(sub, layoutFill); + + subscriptionsPanel.setSelectedIndex(subscriptionsPanel.getTabCount() - 1); + mainTabs.setSelectedIndex(1); + + } + + @Override + public void onUnsubscribe(String spuid) { + subscriptionsPanel.remove(subscriptions.get(spuid)); + subscriptions.remove(spuid); + subscriptionResultsDM.remove(spuid); + subscriptionResultsLabels.remove(spuid); + subscriptionResultsTables.remove(spuid); + } } private class SortedListModel extends AbstractListModel { @@ -1342,7 +1417,7 @@ public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) { try { subscribe(); - } catch (IOException | SEPAPropertiesException e1) { + } catch (IOException | SEPAPropertiesException | NumberFormatException | SEPAProtocolException | SEPASecurityException e1) { logger.error(e1.getMessage()); } } @@ -1370,8 +1445,7 @@ public void actionPerformed(ActionEvent e) { bindingsResultsTable.setCellSelectionEnabled(true); bindingsRender.setNamespaces(namespacesDM); - subscriptions = new JTabbedPane(JTabbedPane.TOP); - mainTabs.addTab("Active subscriptions", null, subscriptions, null); + mainTabs.addTab("Active subscriptions", null, subscriptionsPanel, null); JPanel namespaces = new JPanel(); mainTabs.addTab("Namespaces", null, namespaces, null); @@ -1560,7 +1634,7 @@ protected void clear() { } } - protected void subscribe() throws IOException, SEPAPropertiesException { + protected void subscribe() throws IOException, SEPAPropertiesException, NumberFormatException, SEPAProtocolException, SEPASecurityException { Bindings bindings = new Bindings(); for (int row = 0; row < queryForcedBindings.getRowCount(); row++) { String type = queryForcedBindings.getValueAt(row, 2).toString(); @@ -1574,97 +1648,7 @@ else if (type.toUpperCase().equals("BNODE")) bindings.addBinding(variable, new RDFTermLiteral(value, type)); } - try { - Instant start = Instant.now(); - Response ret = sepaClient.subscribe(queryID, querySPARQL.getText(), bindings, handler); - Instant stop = Instant.now(); - - if (ret.isError()) - logger.error(ret.toString() + String.format(" (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); - else { - String spuid = ((SubscribeResponse) ret).getSpuid(); - BindingsResults results = ((SubscribeResponse) ret).getBindingsResults(); - - logger.info(String.format("Subscribed in %d ms (first results: %d)", - (stop.toEpochMilli() - start.toEpochMilli()), results.size())); - - // Subscription panel - JPanel sub = new JPanel(); - - // Layout - GridBagConstraints layoutFill = new GridBagConstraints(); - layoutFill.fill = GridBagConstraints.BOTH; - sub.setLayout(new BoxLayout(sub, BoxLayout.Y_AXIS)); - sub.setName(queryList.getSelectedValue()); - - // Query label - JLabel queryLabel = new JLabel( - "" + querySPARQL.getText() + " forced bindings: " + bindings.toString() + ""); - queryLabel.setFont(new Font("Arial", Font.BOLD, 14)); - - // Info label - JLabel info = new JLabel("Info"); - info.setText(String.format("Results: %d (%d ms)", results.size(), - (stop.toEpochMilli() - start.toEpochMilli()))); - subscriptionResultsLabels.put(spuid, info); - - // Unsubscribe button - JButton unsubscribeButton = new JButton(spuid); - unsubscribeButton.setEnabled(true); - unsubscribeButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Response response = null; - try { - response = sepaClient.unsubscribe(spuid); - } catch (SEPASecurityException | IOException | SEPAPropertiesException e1) { - logger.error(e1.getMessage()); - return; - } - - if (response.isUnsubscribeResponse()) { - subscriptions.remove(sub); - subscriptionResultsDM.remove(spuid); - subscriptionResultsLabels.remove(spuid); - subscriptionResultsTables.remove(spuid); - } else - subscriptionResultsLabels.get(spuid).setText(response.toString()); - } - }); - - // Results table - subscriptionResultsDM.put(spuid, new BindingsTableModel()); - - JTable bindingsResultsTable = new JTable(subscriptionResultsDM.get(spuid)); - JScrollPane bindingsResults = new JScrollPane(); - bindingsResults.setViewportView(bindingsResultsTable); - - bindingsResultsTable.setDefaultRenderer(Object.class, bindingsRender); - bindingsResultsTable.setAutoCreateRowSorter(true); - bindingsResultsTable.registerKeyboardAction(new CopyAction(), - KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), - JComponent.WHEN_FOCUSED); - bindingsResultsTable.setCellSelectionEnabled(true); - - subscriptionResultsTables.put(spuid, bindingsResultsTable); - - subscriptionResultsDM.get(spuid).setAddedResults(results, spuid); - - // Add all elements - sub.add(queryLabel); - sub.add(unsubscribeButton); - sub.add(bindingsResults); - sub.add(info); - - // Add tab - subscriptions.add(sub, layoutFill); - - subscriptions.setSelectedIndex(subscriptions.getTabCount() - 1); - mainTabs.setSelectedIndex(1); - } - } catch (SEPAProtocolException | SEPASecurityException e) { - logger.error(e.getMessage()); - } - + sepaClient.subscribe(queryID, querySPARQL.getText(), bindings, handler,Integer.parseInt(timeout.getText())); } protected void query() throws SEPAPropertiesException { diff --git a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java index 75758902..b49d85bf 100644 --- a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java +++ b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java @@ -55,12 +55,12 @@ public static void init() { } @Test - public void chatTest() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException { + public void chatTest() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException, IOException { deleteAllClients(); registerClients(); users = new Users(); - assertFalse("Users failed to join chat",!users.joinChat()); + users.joinChat(); for (String user : users.getUsers()) { ChatClient client = null; From c4dcb340780d9c31d10579841a64be30bc9cf439 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Mon, 30 Jul 2018 21:56:05 +0200 Subject: [PATCH 35/76] Secure JUnit test PASSED --- .../wot/sepa/api/SPARQL11SEProtocol.java | 27 +- .../wot/sepa/api/SubscriptionProtocol.java | 15 +- .../WebsocketSubscriptionProtocol.java | 152 ---- .../{ => websocket}/SEPAWebsocketClient.java | 37 +- .../WebsocketSubscriptionProtocol.java | 105 +++ .../exceptions/SEPAPropertiesException.java | 4 + .../commons/protocol/SPARQL11Protocol.java | 455 ++++++------ .../sepa/commons/response/ErrorResponse.java | 171 ++++- .../sepa/commons/response/JWTResponse.java | 12 +- .../wot/sepa/commons/response/Response.java | 16 +- .../security/AuthenticationProperties.java | 163 ++--- .../commons/security/SEPASecurityManager.java | 261 ++++--- .../arces/wot/sepa/pattern/Consumer.java | 2 +- .../arces/wot/sepa/pattern/GenericClient.java | 11 +- .../unibo/arces/wot/sepa/timing/Timings.java | 2 +- .../wot/sepa/api/ConfigurationProvider.java | 2 +- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 662 +++++++----------- .../unibo/arces/wot/sepa/api/Publisher.java | 52 +- .../unibo/arces/wot/sepa/api/Subscriber.java | 143 ++-- .../it/unibo/arces/wot/sepa/api/Sync.java | 97 +++ .../websocket/ITSEPAWebsocketClient.java | 56 ++ .../protocol/websocket/ITWebSocketClient.java | 61 ++ .../ITWebSocketSubscriptionProtocol.java | 192 ++--- .../protocol/websocket/WebsocketClient.java | 39 -- client-api/src/test/resources/log4j2.xml | 6 +- .../src/test/resources/sepatest-secure.jsap | 6 +- .../wot/sepa/engine/bean/SchedulerBeans.java | 10 + .../engine/bean/SubscribeProcessorBeans.java | 30 +- .../wot/sepa/engine/bean/WebsocketBeans.java | 43 ++ .../arces/wot/sepa/engine/core/Engine.java | 5 +- .../sepa/engine/core/EngineProperties.java | 115 ++- .../dependability/AuthorizationManager.java | 346 +++++---- .../dependability/DependabilityManager.java | 53 +- .../engine/processing/QueryProcessor.java | 3 +- .../processing/SubscribeProcessingThread.java | 121 ++-- .../SubscribeProcessingThreadMBean.java | 4 +- .../engine/processing/UpdateProcessor.java | 3 +- .../processing/subscriptions/SPUManager.java | 2 +- .../processing/subscriptions/SPUNaive.java | 2 +- .../engine/protocol/http/HttpUtilities.java | 8 +- .../sepa/engine/protocol/http/HttpsGate.java | 4 +- .../http/handler/JWTRequestHandler.java | 23 +- .../protocol/http/handler/QueryHandler.java | 8 + .../http/handler/RegisterHandler.java | 30 +- .../http/handler/SPARQL11Handler.java | 27 +- .../http/handler/SPARQL11ResponseHandler.java | 2 +- .../http/handler/SecureQueryHandler.java | 56 +- .../http/handler/SecureUpdateHandler.java | 57 +- .../protocol/http/handler/UpdateHandler.java | 8 + .../websocket/SecureWebsocketServer.java | 169 +++-- .../websocket/WebsocketEventHandler.java | 6 + .../protocol/websocket/WebsocketServer.java | 38 +- .../websocket/WebsocketServerMBean.java | 8 + .../wot/sepa/engine/scheduling/Scheduler.java | 16 +- .../engine/scheduling/SchedulerMBean.java | 4 + .../engine/scheduling/SchedulerQueue.java | 32 +- .../arces/wot/sepa/engine/timing/Timings.java | 2 +- engine/src/main/resources/log4j2.xml | 6 +- tools/arces-demo.jsap | 9 + tools/endpoint.jpar | 1 + .../unibo/arces/wot/sepa/tools/Dashboard.java | 6 +- 61 files changed, 2306 insertions(+), 1700 deletions(-) delete mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java rename client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/{ => websocket}/SEPAWebsocketClient.java (73%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java create mode 100644 tools/endpoint.jpar diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java index a71a8322..e82fae77 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java @@ -31,7 +31,6 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; /** * This class implements the SPARQL 1.1 Secure event protocol with SPARQL 1.1 @@ -45,31 +44,17 @@ public class SPARQL11SEProtocol extends SPARQL11Protocol { private static final Logger logger = LogManager.getLogger(); - private SubscriptionProtocol subscriptionProtocol; - - public SPARQL11SEProtocol(SubscriptionProtocol protocol, SEPASecurityManager sm) throws SEPAProtocolException { - super(sm); - - if (protocol == null) throw new IllegalArgumentException("Protocol is null"); - if (!protocol.isSecure()) throw new SEPAProtocolException(new IllegalArgumentException("Mixing secure and not secure protocols is not allowed")); - - this.subscriptionProtocol = protocol; - } + private final SubscriptionProtocol subscriptionProtocol; public SPARQL11SEProtocol(SubscriptionProtocol protocol) throws SEPAProtocolException { - if (protocol == null ) { - logger.error("One or more arguments are null"); - throw new IllegalArgumentException("One or more arguments are null"); - } - - if (protocol.isSecure()) throw new SEPAProtocolException(new IllegalArgumentException("Security parameters are missing")); + super(protocol.getSecurityManager()); this.subscriptionProtocol = protocol; } - public boolean isSecure() { - return subscriptionProtocol.isSecure(); - } +// public boolean isSecure() { +// return super.isSecure() && subscriptionProtocol.isSecure(); +// } /** * Subscribe with a SPARQL 1.1 Subscription language. All the notification will @@ -81,7 +66,7 @@ public boolean isSecure() { * @throws SEPAProtocolException */ public void subscribe(SubscribeRequest request) throws SEPAProtocolException { - logger.debug(request.toString()); + logger.info("SUBSCRIBE: "+request.toString()); subscriptionProtocol.subscribe(request); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java index 7bfe7607..b8dc5c69 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java @@ -1,14 +1,19 @@ package it.unibo.arces.wot.sepa.api; +import java.io.Closeable; + import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -public abstract class SubscriptionProtocol { - protected ISubscriptionHandler handler; +public abstract class SubscriptionProtocol implements Closeable{ + protected final ISubscriptionHandler handler; + protected final SEPASecurityManager sm; - public SubscriptionProtocol(ISubscriptionHandler handler) { + public SubscriptionProtocol(ISubscriptionHandler handler,SEPASecurityManager sm) { this.handler = handler; + this.sm = sm; } public abstract void close(); @@ -17,5 +22,7 @@ public SubscriptionProtocol(ISubscriptionHandler handler) { public abstract void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException; - public abstract boolean isSecure(); + public final SEPASecurityManager getSecurityManager() { + return sm; + } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java deleted file mode 100644 index 14a9e689..00000000 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/WebsocketSubscriptionProtocol.java +++ /dev/null @@ -1,152 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocols; - -import java.io.IOException; -import java.net.Socket; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.channels.NotYetConnectedException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; -//import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -//import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -public class WebsocketSubscriptionProtocol extends SubscriptionProtocol { - protected final Logger logger = LogManager.getLogger(); - - private SEPAWebsocketClient client = null; - - private Socket secure = null; - private URI url; - - public WebsocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm, - ISubscriptionHandler handler) throws SEPAProtocolException { - super(handler); - if (sm == null) { - logger.error("Security manager is null"); - throw new IllegalArgumentException("Security manager is null"); - } - - try { - url = new URI("wss://" + host + ":" + port + path); - } catch (URISyntaxException e1) { - throw new SEPAProtocolException(e1); - } - - try { - secure = sm.getSSLSocket(); - } catch (IOException e) { - throw new SEPAProtocolException(e); - } - } - - public WebsocketSubscriptionProtocol(String host, String path, SEPASecurityManager sm, ISubscriptionHandler handler) - throws SEPAProtocolException { - super(handler); - if (sm == null) { - logger.error("Security manager is null"); - throw new IllegalArgumentException("Security manager is null"); - } - - try { - url = new URI("wss://" + host + path); - } catch (URISyntaxException e1) { - throw new SEPAProtocolException(e1); - } - - try { - secure = sm.getSSLSocket(); - } catch (IOException e) { - throw new SEPAProtocolException(e); - } - } - - public WebsocketSubscriptionProtocol(String host, int port, String path, ISubscriptionHandler handler) - throws SEPAProtocolException { - super(handler); - try { - url = new URI("ws://" + host + ":" + port + path); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } - } - - public WebsocketSubscriptionProtocol(String host, String path, ISubscriptionHandler handler) - throws SEPAProtocolException { - super(handler); - try { - url = new URI("ws://" + host + path); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } - } - - private void connect() throws SEPAProtocolException { - if (client == null) { - if (secure != null) - client = new SEPAWebsocketClient(url, handler, secure); - else - client = new SEPAWebsocketClient(url, handler); - - try { - if (!client.connectBlocking()) { - logger.error("Failed to connect"); - throw new SEPAProtocolException("Failed to connect"); - } - } catch (InterruptedException e) { - throw new SEPAProtocolException(e); - } - - logger.debug("Connected"); - } - } - - @Override - public void subscribe(SubscribeRequest request) throws SEPAProtocolException { - connect(); - - try { - client.send(request.toString()); - } catch (NotYetConnectedException e) { - throw new SEPAProtocolException(e); - } - // try { - // connect(); - // } catch (SEPAProtocolException e) { - // return new ErrorResponse(500,"Failed to connect"); - // } - // logger.debug("Subscribe: " + request.toString()); - // return client.sendAndReceive(request.toString(), request.getTimeout()); - } - - @Override - public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { - try { - client.send(request.toString()); - } catch (NotYetConnectedException e) { - throw new SEPAProtocolException(e); - } - - // logger.debug("Unsubscribe: " + request.toString()); - // return client.sendAndReceive(request.toString(), request.getTimeout()); - } - - @Override - public boolean isSecure() { - return secure != null; - } - - @Override - public void close() { - if (client != null) - client.close(); - } - -} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/SEPAWebsocketClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/SEPAWebsocketClient.java similarity index 73% rename from client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/SEPAWebsocketClient.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/SEPAWebsocketClient.java index fcd196b5..6e753250 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/SEPAWebsocketClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/SEPAWebsocketClient.java @@ -1,4 +1,4 @@ -package it.unibo.arces.wot.sepa.api.protocols; +package it.unibo.arces.wot.sepa.api.protocols.websocket; import java.net.Socket; import java.net.URI; @@ -15,19 +15,14 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; -class SEPAWebsocketClient extends WebSocketClient { +public class SEPAWebsocketClient extends WebSocketClient { protected final Logger logger = LogManager.getLogger(); - private ISubscriptionHandler handler; - + private final ISubscriptionHandler handler; + public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler, Socket secure) { super(wsUrl); - if (handler == null) { - logger.fatal("Notification handler is null. Client cannot be initialized"); - throw new IllegalArgumentException("Notificaton handler is null"); - } - this.handler = handler; setSocket(secure); @@ -36,32 +31,30 @@ public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler, Socket secur public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { super(wsUrl); - if (handler == null) { - logger.fatal("Notification handler is null. Client cannot be initialized"); - throw new IllegalArgumentException("Notificaton handler is null"); - } - this.handler = handler; } @Override public void onOpen(ServerHandshake handshakedata) { - logger.debug("@onOpen STATE: " + getReadyState()); + logger.debug("@onOpen: "+handshakedata.getHttpStatusMessage()); } @Override public void onClose(int code, String reason, boolean remote) { - logger.debug( - "@onClose code:" + code + " reason:" + reason + " remote:" + remote + " state:" + getReadyState()); - if (handler != null) + logger.debug("@onClose code:" + code + " reason:" + reason + " remote:" + remote); + + try { handler.onBrokenConnection(); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } } @Override public void onError(Exception ex) { - ErrorResponse error = new ErrorResponse(500, "onError: " + ex.getMessage()); + ErrorResponse error = new ErrorResponse(500, "Exception", ex.getMessage()); - logger.debug("@onError: " + error + " STATE: " + getReadyState()); + logger.debug("@onError: " + error); try { handler.onError(error); @@ -72,7 +65,7 @@ public void onError(Exception ex) { @Override public void onMessage(String message) { - logger.debug("@onMessage " + message); + logger.debug("@onMessage: " + message); // Parse message JsonObject jsonMessage = null; @@ -104,7 +97,7 @@ public void onMessage(String message) { } } else if (jsonMessage.has("error")) { try{ - handler.onError(new ErrorResponse(jsonMessage)); + handler.onError(new ErrorResponse(jsonMessage.get("status_code").getAsInt(),jsonMessage.get("error").getAsString(),jsonMessage.get("error_description").getAsString())); } catch(Exception e) { logger.error("Handler is null "+e.getMessage()); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java new file mode 100644 index 00000000..185b9dbe --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java @@ -0,0 +1,105 @@ +package it.unibo.arces.wot.sepa.api.protocols.websocket; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.NotYetConnectedException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +public class WebsocketSubscriptionProtocol extends SubscriptionProtocol { + protected final Logger logger = LogManager.getLogger(); + + private final SEPAWebsocketClient client; + + public WebsocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm, + ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException { + super(handler,sm); + + try { + client = new SEPAWebsocketClient(new URI("wss://" + host + ":" + port + path), handler, sm.getSSLSocket()); + } catch (URISyntaxException e) { + throw new SEPAProtocolException(e); + } + } + + public WebsocketSubscriptionProtocol(String host, String path, SEPASecurityManager sm, ISubscriptionHandler handler) + throws SEPAProtocolException, SEPASecurityException { + super(handler,sm); + + try { + client = new SEPAWebsocketClient(new URI("wss://" + host + path), handler, sm.getSSLSocket()); + } catch (URISyntaxException e) { + throw new SEPAProtocolException(e); + } + } + + public WebsocketSubscriptionProtocol(String host, int port, String path, ISubscriptionHandler handler) + throws SEPAProtocolException { + super(handler,null); + + try { + client = new SEPAWebsocketClient(new URI("ws://" + host + ":" + port + path), handler); + } catch (URISyntaxException e) { + throw new SEPAProtocolException(e); + } + } + + public WebsocketSubscriptionProtocol(String host, String path, ISubscriptionHandler handler) + throws SEPAProtocolException { + super(handler,null); + + try { + client = new SEPAWebsocketClient(new URI("ws://" + host + path), handler); + } catch (URISyntaxException e) { + throw new SEPAProtocolException(e); + } + } + + @Override + public void subscribe(SubscribeRequest request) throws SEPAProtocolException { + logger.debug("@subscribe: "+request); + if (!client.isOpen()) { + logger.debug("connectBlocking..."); + try { + if(client.connectBlocking()) logger.debug("Connected");; + } catch (InterruptedException e1) { + throw new SEPAProtocolException(e1); + } + } + + try { + logger.debug("Send"); + client.send(request.toString()); + } catch (NotYetConnectedException e) { + logger.error(e.getMessage()); + throw new SEPAProtocolException(e); + } + } + + @Override + public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { + logger.debug("@unsubscribe: "+request); + try { + client.send(request.toString()); + } catch (NotYetConnectedException e) { + logger.error(e.getMessage()); + throw new SEPAProtocolException(e); + } + } + + @Override + public void close() { + logger.debug("Close"); + client.close(); + } + +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java index 17cce392..6c52344f 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/exceptions/SEPAPropertiesException.java @@ -11,4 +11,8 @@ public SEPAPropertiesException(Throwable e){ super.initCause(e); } + public SEPAPropertiesException(String string) { + super.initCause(new Exception(string)); + } + } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index da7c8164..c3ac84dc 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -56,6 +56,8 @@ import org.apache.logging.log4j.Logger; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import org.apache.logging.log4j.LogManager; @@ -76,7 +78,7 @@ public class SPARQL11Protocol implements java.io.Closeable { protected CloseableHttpClient httpClient = HttpClients.createDefault(); /** The security manager */ - protected SEPASecurityManager sm; + protected final SEPASecurityManager sm; public SPARQL11Protocol(SEPASecurityManager sm) { if (sm == null) @@ -86,12 +88,13 @@ public SPARQL11Protocol(SEPASecurityManager sm) { } public SPARQL11Protocol() { + this.sm = null; httpClient = HttpClients.createDefault(); } - public boolean isSecure() { - return sm != null; - } +// public boolean isSecure() { +// return sm != null; +// } private Response executeRequest(HttpUriRequest req, Request request) { CloseableHttpResponse httpResponse = null; @@ -128,40 +131,144 @@ private Response executeRequest(HttpUriRequest req, Request request) { // http://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/fundamentals.html#d5e279 } catch (IOException e) { if (e instanceof InterruptedIOException) { - return new ErrorResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "InterruptedIOException",e.getMessage()); } if (e instanceof UnknownHostException) { - return new ErrorResponse(HttpStatus.SC_NOT_FOUND, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_NOT_FOUND, "UnknownHostException",e.getMessage()); } if (e instanceof ConnectTimeoutException) { - return new ErrorResponse(HttpStatus.SC_REQUEST_TIMEOUT, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_REQUEST_TIMEOUT,"ConnectTimeoutException", e.getMessage()); } if (e instanceof SSLException) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "SSLException",e.getMessage()); } - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "IOException",e.getMessage()); } finally { try { if (httpResponse != null) httpResponse.close(); } catch (IOException e) { - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"IOException",e.getMessage()); } responseEntity = null; } - if (responseCode >= 400) - return new ErrorResponse(responseCode, responseBody); + JsonObject ret = null; + if (responseCode >= 400) { + // Parse the JSON error response body + try{ + ret = new JsonParser().parse(responseBody).getAsJsonObject(); + } + catch(Exception e) { + return new ErrorResponse(HttpStatus.SC_UNPROCESSABLE_ENTITY,"JsonParseException","Not a valid JSON: "+responseBody); + } + return new ErrorResponse(ret.get("status_code").getAsInt(), ret.get("error").getAsString(),ret.get("error_description").getAsString()); + } if (request.getClass().equals(UpdateRequest.class)) return new UpdateResponse(responseBody); - try { - return new QueryResponse(new JsonParser().parse(responseBody).getAsJsonObject()); - } catch (Exception e) { - return new ErrorResponse(HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE, e.getMessage()); + + try{ + ret = new JsonParser().parse(responseBody).getAsJsonObject(); + } + catch(JsonParseException e) { + return new ErrorResponse(HttpStatus.SC_UNPROCESSABLE_ENTITY, "JsonParseException",e.getMessage()); + } + return new QueryResponse(ret); + } + + /** + * Implements a SPARQL 1.1 query operation + * (https://www.w3.org/TR/sparql11-protocol/) + * + *
    +	 * query via GET
    +	 * - HTTP Method: GET
    +	 * - Query String Parameters: query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
    +	 * - Request Content Type: None
    +	 * - Request Message Body: None
    +	 * 
    +	 * query via URL-encoded POST 
    +	 * - HTTP Method: POST
    +	 * - Query String Parameters: None
    +	 * - Request Content Type: application/x-www-form-urlencoded
    +	 * - Request Message Body: URL-encoded, ampersand-separated query parameters. query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
    +	 * 
    +	 * query via POST directly
    +	 * - HTTP Method: POST
    +	 * - Query String parameters: default-graph-uri (0 or more). named-graph-uri (0 or more)
    +	 * - Request Content Type: application/sparql-query
    +	 * - Request Message Body: Unencoded SPARQL update request string
    +	 *
    +	 * 2.1.4 Specifying an RDF Dataset
    +	 * 
    +	 * A SPARQL query is executed against an RDF Dataset. The RDF Dataset for a query may be specified either 
    +	 * via the default-graph-uri and named-graph-uri parameters in the SPARQL Protocol or in the SPARQL query 
    +	 * string using the FROM and FROM NAMED keywords. If different RDF Datasets are specified in both the protocol 
    +	 * request and the SPARQL query string, then the SPARQL service must execute the query using the RDF Dataset 
    +	 * given in the protocol request.
    +	 * 
    +	 * Note that a service may reject a query with HTTP response code 400 if the service does not allow protocol 
    +	 * clients to specify the RDF Dataset. If an RDF Dataset is not specified in either the protocol request or 
    +	 * the SPARQL query string, then implementations may execute the query against an implementation-defined default RDF dataset.
    +	 * 
    +	 * QUERY 2.1.5 Accepted Response Formats
    +	 * 
    +	 * Protocol clients should use HTTP content negotiation [RFC2616] to request
    +	 * response formats that the client can consume. See below for more on
    +	 * potential response formats.
    +	 * 
    +	 * 2.1.6 Success Responses
    +	 * 
    +	 * The SPARQL Protocol uses the response status codes defined in HTTP to
    +	 * indicate the success or failure of an operation. Consult the HTTP
    +	 * specification [RFC2616] for detailed definitions of each status code.
    +	 * While a protocol service should use a 2XX HTTP response code for a
    +	 * successful query, it may choose instead to use a 3XX response code as per
    +	 * HTTP.
    +	 * 
    +	 * The response body of a successful query operation with a 2XX response is
    +	 * either:
    +	 * 
    +	 * a SPARQL Results Document in XML, JSON, or CSV/TSV format (for SPARQL
    +	 * Query forms SELECT and ASK); or, an RDF graph [RDF-CONCEPTS] serialized,
    +	 * for example, in the RDF/XML syntax [RDF-XML], or an equivalent RDF graph
    +	 * serialization, for SPARQL Query forms DESCRIBE and CONSTRUCT). The
    +	 * content type of the response to a successful query operation must be the
    +	 * media type defined for the format of the response body.
    +	 * 
    +	 * 2.1.7 Failure Responses
    +	 * 
    +	 * The HTTP response codes applicable to an unsuccessful query operation
    +	 * include:
    +	 * 
    +	 * 400 if the SPARQL query supplied in the request is not a legal sequence
    +	 * of characters in the language defined by the SPARQL grammar; or, 500 if
    +	 * the service fails to execute the query. SPARQL Protocol services may also
    +	 * return a 500 response code if they refuse to execute a query. This
    +	 * response does not indicate whether the server may or may not process a
    +	 * subsequent, identical request or requests. The response body of a failed
    +	 * query request is implementation defined. Implementations may use HTTP
    +	 * content negotiation to provide human-readable or machine-processable (or
    +	 * both) information about the failed query request.
    +	 * 
    +	 * A protocol service may use other 4XX or 5XX HTTP response codes for other
    +	 * failure conditions, as per HTTP.
    +	 *
    +	 * 
    + */ + public Response query(QueryRequest req) { + switch (req.getHttpMethod()) { + case GET: + return get(req); + case POST: + return post(req); + case URL_ENCODED_POST: + return post(req); } + return post(req); } /** @@ -221,7 +328,7 @@ public Response update(UpdateRequest req) { case URL_ENCODED_POST: return post(req); default: - return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, + return new ErrorResponse(HttpStatus.SC_METHOD_NOT_ALLOWED,"unsupported_method", "SPARQL 1.1 Update supports POST method only"); } } @@ -287,7 +394,7 @@ private Response post(UpdateRequest req) { } } catch (UnsupportedEncodingException e) { logger.error(e.getMessage()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"UnsupportedEncodingException", e.getMessage()); } // Body @@ -296,9 +403,12 @@ private Response post(UpdateRequest req) { else requestEntity = new StringEntity("update=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"), Consts.UTF_8); } - } catch (URISyntaxException | UnsupportedEncodingException e) { + } catch (URISyntaxException e) { logger.error(e.getMessage()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "URISyntaxException",e.getMessage()); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "UnsupportedEncodingException",e.getMessage()); } // Accept header @@ -314,198 +424,6 @@ private Response post(UpdateRequest req) { return executeRequest(post, req); } - // private Response patchVirtuoso(UpdateRequest req) { - // // 1) "INSERT DATA" is not supported. Only INSERT (also if the WHERE is not - // // present). - // String fixedSparql = req.getSPARQL(); - // Pattern p = null; - // try { - // p = Pattern.compile( - // "(?.*)(delete)([^{]*)(?.*)(insert)([^{]*)(?.*)|(?.*)(delete)(?[^{]*)(?.*)|(?.*)(insert)([^{]*)(?.*)", - // Pattern.CASE_INSENSITIVE); - // - // Matcher m = p.matcher(req.getSPARQL()); - // if (m.matches()) { - // if (m.group("update") != null) { - // fixedSparql = m.group("update") + " DELETE " + m.group("udtriples") + " - // INSERT " - // + m.group("uitriples"); - // } else if (m.group("insert") != null) { - // fixedSparql = m.group("insert") + " INSERT " + m.group("itriples"); - // } else { - // if (m.group("where") != null) { - // if (m.group("where").toLowerCase().contains("where")) { - // fixedSparql = m.group("delete") + " DELETE " + m.group("where") + - // m.group("dtriples"); - // } - // else - // fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples"); - // } - // else fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples"); - // } - // } - // } catch (Exception e) { - // return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, - // e.getMessage()); - // } - // - // // 2) SPARQL 1.1 Update are issued as GET request (like for a SPARQL 1.1 - // Query) - // String query; - // try { - // // custom "format" parameter - // query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format=" - // + URLEncoder.encode(req.getAcceptHeader(), "UTF-8"); - // } catch (UnsupportedEncodingException e1) { - // logger.error(e1.getMessage()); - // return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, - // e1.getMessage()); - // } - // - // // 3) Named-graphs specified like a query - // String graphs = ""; - // try { - // if (req.getUsingGraphUri() != null) { - // - // graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(), - // "UTF-8"); - // - // if (req.getUsingNamedGraphUri() != null) { - // graphs += "&named-graph-uri=" + - // URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8"); - // } - // } else if (req.getUsingNamedGraphUri() != null) { - // graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(), - // "UTF-8"); - // } - // } catch (UnsupportedEncodingException e) { - // logger.error(e.getMessage()); - // return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR, - // e.getMessage()); - // } - // - // if (!graphs.equals("")) - // query += "&" + graphs; - // - // logger.debug("Query: " + query); - // - // // Setting URL - // String scheme = req.getScheme(); - // String host = req.getHost(); - // int port = req.getPort(); - // String queryPath = req.getPath(); - // - // String url; - // if (port != -1) - // url = scheme + "://" + host + ":" + port + queryPath + "?" + query; - // else - // url = scheme + "://" + host + queryPath + "?" + query; - // - // HttpGet get; - // get = new HttpGet(url); - // - // get.setHeader("Accept", req.getAcceptHeader()); - // - // RequestConfig requestConfig = - // RequestConfig.custom().setSocketTimeout(req.getTimeout()) - // .setConnectTimeout(req.getTimeout()).build(); - // get.setConfig(requestConfig); - // - // return executeRequest(get, req); - // } - - /** - * Implements a SPARQL 1.1 query operation - * (https://www.w3.org/TR/sparql11-protocol/) - * - *
    -	 * query via GET
    -	 * - HTTP Method: GET
    -	 * - Query String Parameters: query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
    -	 * - Request Content Type: None
    -	 * - Request Message Body: None
    -	 * 
    -	 * query via URL-encoded POST 
    -	 * - HTTP Method: POST
    -	 * - Query String Parameters: None
    -	 * - Request Content Type: application/x-www-form-urlencoded
    -	 * - Request Message Body: URL-encoded, ampersand-separated query parameters. query (exactly 1). default-graph-uri (0 or more). named-graph-uri (0 or more)
    -	 * 
    -	 * query via POST directly
    -	 * - HTTP Method: POST
    -	 * - Query String parameters: default-graph-uri (0 or more). named-graph-uri (0 or more)
    -	 * - Request Content Type: application/sparql-query
    -	 * - Request Message Body: Unencoded SPARQL update request string
    -	 *
    -	 * 2.1.4 Specifying an RDF Dataset
    -	 * 
    -	 * A SPARQL query is executed against an RDF Dataset. The RDF Dataset for a query may be specified either 
    -	 * via the default-graph-uri and named-graph-uri parameters in the SPARQL Protocol or in the SPARQL query 
    -	 * string using the FROM and FROM NAMED keywords. If different RDF Datasets are specified in both the protocol 
    -	 * request and the SPARQL query string, then the SPARQL service must execute the query using the RDF Dataset 
    -	 * given in the protocol request.
    -	 * 
    -	 * Note that a service may reject a query with HTTP response code 400 if the service does not allow protocol 
    -	 * clients to specify the RDF Dataset. If an RDF Dataset is not specified in either the protocol request or 
    -	 * the SPARQL query string, then implementations may execute the query against an implementation-defined default RDF dataset.
    -	 * 
    -	 * QUERY 2.1.5 Accepted Response Formats
    -	 * 
    -	 * Protocol clients should use HTTP content negotiation [RFC2616] to request
    -	 * response formats that the client can consume. See below for more on
    -	 * potential response formats.
    -	 * 
    -	 * 2.1.6 Success Responses
    -	 * 
    -	 * The SPARQL Protocol uses the response status codes defined in HTTP to
    -	 * indicate the success or failure of an operation. Consult the HTTP
    -	 * specification [RFC2616] for detailed definitions of each status code.
    -	 * While a protocol service should use a 2XX HTTP response code for a
    -	 * successful query, it may choose instead to use a 3XX response code as per
    -	 * HTTP.
    -	 * 
    -	 * The response body of a successful query operation with a 2XX response is
    -	 * either:
    -	 * 
    -	 * a SPARQL Results Document in XML, JSON, or CSV/TSV format (for SPARQL
    -	 * Query forms SELECT and ASK); or, an RDF graph [RDF-CONCEPTS] serialized,
    -	 * for example, in the RDF/XML syntax [RDF-XML], or an equivalent RDF graph
    -	 * serialization, for SPARQL Query forms DESCRIBE and CONSTRUCT). The
    -	 * content type of the response to a successful query operation must be the
    -	 * media type defined for the format of the response body.
    -	 * 
    -	 * 2.1.7 Failure Responses
    -	 * 
    -	 * The HTTP response codes applicable to an unsuccessful query operation
    -	 * include:
    -	 * 
    -	 * 400 if the SPARQL query supplied in the request is not a legal sequence
    -	 * of characters in the language defined by the SPARQL grammar; or, 500 if
    -	 * the service fails to execute the query. SPARQL Protocol services may also
    -	 * return a 500 response code if they refuse to execute a query. This
    -	 * response does not indicate whether the server may or may not process a
    -	 * subsequent, identical request or requests. The response body of a failed
    -	 * query request is implementation defined. Implementations may use HTTP
    -	 * content negotiation to provide human-readable or machine-processable (or
    -	 * both) information about the failed query request.
    -	 * 
    -	 * A protocol service may use other 4XX or 5XX HTTP response codes for other
    -	 * failure conditions, as per HTTP.
    -	 *
    -	 * 
    - */ - public Response query(QueryRequest req) { - switch (req.getHttpMethod()) { - case GET: - return get(req); - case POST: - return post(req); - case URL_ENCODED_POST: - return post(req); - } - return post(req); - } - /** *
     	 *                               HTTP Method   Query String Parameters           Request Content Type                Request Message Body
    @@ -568,7 +486,7 @@ private Response post(QueryRequest req) {
     					}
     				} catch (UnsupportedEncodingException e) {
     					logger.error(e.getMessage());
    -					return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    +					return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"UnsupportedEncodingException", e.getMessage());
     				}
     				
     				// Body
    @@ -577,9 +495,12 @@ private Response post(QueryRequest req) {
     				else requestEntity = new StringEntity("query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8"),
     						Consts.UTF_8);
     			}
    -		} catch (URISyntaxException | UnsupportedEncodingException e) {
    +		} catch (URISyntaxException e) {
     			logger.error(e.getMessage());
    -			return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    +			return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "URISyntaxException",e.getMessage());
    +		} catch (UnsupportedEncodingException e) {
    +			logger.error(e.getMessage());
    +			return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "UnsupportedEncodingException",e.getMessage());
     		}
     
     		post.setHeader("Accept", req.getAcceptHeader());
    @@ -608,7 +529,7 @@ private Response get(QueryRequest req) {
     			query = "query=" + URLEncoder.encode(req.getSPARQL(), "UTF-8");
     		} catch (UnsupportedEncodingException e1) {
     			logger.error(e1.getMessage());
    -			return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e1.getMessage());
    +			return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "UnsupportedEncodingException",e1.getMessage());
     		}
     
     		String graphs = "";
    @@ -625,7 +546,7 @@ private Response get(QueryRequest req) {
     			}
     		} catch (UnsupportedEncodingException e) {
     			logger.error(e.getMessage());
    -			return new ErrorResponse( HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    +			return new ErrorResponse( HttpStatus.SC_INTERNAL_SERVER_ERROR, "UnsupportedEncodingException",e.getMessage());
     		}
     
     		if (!graphs.equals(""))
    @@ -664,3 +585,103 @@ public void close() {
     		}
     	}
     }
    +
    +// private Response patchVirtuoso(UpdateRequest req) {
    +// // 1) "INSERT DATA" is not supported. Only INSERT (also if the WHERE is not
    +// // present).
    +// String fixedSparql = req.getSPARQL();
    +// Pattern p = null;
    +// try {
    +// p = Pattern.compile(
    +// "(?.*)(delete)([^{]*)(?.*)(insert)([^{]*)(?.*)|(?.*)(delete)(?[^{]*)(?.*)|(?.*)(insert)([^{]*)(?.*)",
    +// Pattern.CASE_INSENSITIVE);
    +//
    +// Matcher m = p.matcher(req.getSPARQL());
    +// if (m.matches()) {
    +// if (m.group("update") != null) {
    +// fixedSparql = m.group("update") + " DELETE " + m.group("udtriples") + "
    +// INSERT "
    +// + m.group("uitriples");
    +// } else if (m.group("insert") != null) {
    +// fixedSparql = m.group("insert") + " INSERT " + m.group("itriples");
    +// } else {
    +// if (m.group("where") != null) {
    +// if (m.group("where").toLowerCase().contains("where")) {
    +// fixedSparql = m.group("delete") + " DELETE " + m.group("where") +
    +// m.group("dtriples");
    +// }
    +// else
    +// fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples");
    +// }
    +// else fixedSparql = m.group("delete") + " DELETE " + m.group("dtriples");
    +// }
    +// }
    +// } catch (Exception e) {
    +// return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR,
    +// e.getMessage());
    +// }
    +//
    +// // 2) SPARQL 1.1 Update are issued as GET request (like for a SPARQL 1.1
    +// Query)
    +// String query;
    +// try {
    +// // custom "format" parameter
    +// query = "query=" + URLEncoder.encode(fixedSparql, "UTF-8") + "&format="
    +// + URLEncoder.encode(req.getAcceptHeader(), "UTF-8");
    +// } catch (UnsupportedEncodingException e1) {
    +// logger.error(e1.getMessage());
    +// return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR,
    +// e1.getMessage());
    +// }
    +//
    +// // 3) Named-graphs specified like a query
    +// String graphs = "";
    +// try {
    +// if (req.getUsingGraphUri() != null) {
    +//
    +// graphs += "default-graph-uri=" + URLEncoder.encode(req.getUsingGraphUri(),
    +// "UTF-8");
    +//
    +// if (req.getUsingNamedGraphUri() != null) {
    +// graphs += "&named-graph-uri=" +
    +// URLEncoder.encode(req.getUsingNamedGraphUri(), "UTF-8");
    +// }
    +// } else if (req.getUsingNamedGraphUri() != null) {
    +// graphs += "named-graph-uri=" + URLEncoder.encode(req.getUsingNamedGraphUri(),
    +// "UTF-8");
    +// }
    +// } catch (UnsupportedEncodingException e) {
    +// logger.error(e.getMessage());
    +// return new ErrorResponse(req.getToken(), HttpStatus.SC_INTERNAL_SERVER_ERROR,
    +// e.getMessage());
    +// }
    +//
    +// if (!graphs.equals(""))
    +// query += "&" + graphs;
    +//
    +// logger.debug("Query: " + query);
    +//
    +// // Setting URL
    +// String scheme = req.getScheme();
    +// String host = req.getHost();
    +// int port = req.getPort();
    +// String queryPath = req.getPath();
    +//
    +// String url;
    +// if (port != -1)
    +// url = scheme + "://" + host + ":" + port + queryPath + "?" + query;
    +// else
    +// url = scheme + "://" + host + queryPath + "?" + query;
    +//
    +// HttpGet get;
    +// get = new HttpGet(url);
    +//
    +// get.setHeader("Accept", req.getAcceptHeader());
    +//
    +// RequestConfig requestConfig =
    +// RequestConfig.custom().setSocketTimeout(req.getTimeout())
    +// .setConnectTimeout(req.getTimeout()).build();
    +// get.setConfig(requestConfig);
    +//
    +// return executeRequest(get, req);
    +// }
    \ No newline at end of file
    diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java
    index c1dc5ad6..6274d075 100644
    --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java
    +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/ErrorResponse.java
    @@ -17,10 +17,8 @@
     */
     package it.unibo.arces.wot.sepa.commons.response;
     
    -import com.google.gson.JsonObject;
     import com.google.gson.JsonPrimitive;
     
    -// TODO: Auto-generated Javadoc
     /**
      * It represents a generic error. If it applies, the use of HTTP status codes is RECOMMENDED 
      * (RFC 2616)

    @@ -51,55 +49,162 @@ 504 Gateway Timeout
    505 HTTP Version Not Supported

    - * The JSON representation of an error response follows:
    - * - * {"error":{
    - * "body" : "Internal Server Error: SPARQL endpoint not found" ,
    - * "code" : 500
    - * }}
    - *
    Body is optional +
    +
    +The JSON representation of an error response follows:
    + + { + "error":"Unless specified otherwise see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol", + "error_description":"Unless specified otherwise, see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol", (OPTIONAL) + "status_code" : the HTTP status code (would be 400 for Oauth 2.0 errors). + } + + 5.2. Error Response + + The authorization server responds with an HTTP 400 (Bad Request) + status code (unless specified otherwise) and includes the following + parameters with the response: + + error + REQUIRED. A single ASCII [USASCII] error code from the + following: + + invalid_request + The request is missing a required parameter, includes an + unsupported parameter value (other than grant type), + repeats a parameter, includes multiple credentials, + utilizes more than one mechanism for authenticating the + client, or is otherwise malformed. + + invalid_client + Client authentication failed (e.g., unknown client, no + client authentication included, or unsupported + authentication method). The authorization server MAY + return an HTTP 401 (Unauthorized) status code to indicate + which HTTP authentication schemes are supported. If the + client attempted to authenticate via the "Authorization" + request header field, the authorization server MUST + respond with an HTTP 401 (Unauthorized) status code and + include the "WWW-Authenticate" response header field + matching the authentication scheme used by the client. + + invalid_grant + The provided authorization grant (e.g., authorization + code, resource owner credentials) or refresh token is + invalid, expired, revoked, does not match the redirection + URI used in the authorization request, or was issued to + another client. + + unauthorized_client + The authenticated client is not authorized to use this + authorization grant type. + + unsupported_grant_type + The authorization grant type is not supported by the + authorization server. + + +Hardt Standards Track [Page 45] + +RFC 6749 OAuth 2.0 October 2012 + + + invalid_scope + The requested scope is invalid, unknown, malformed, or + exceeds the scope granted by the resource owner. + + Values for the "error" parameter MUST NOT include characters + outside the set %x20-21 / %x23-5B / %x5D-7E. + + error_description + OPTIONAL. Human-readable ASCII [USASCII] text providing + additional information, used to assist the client developer in + understanding the error that occurred. + Values for the "error_description" parameter MUST NOT include + characters outside the set %x20-21 / %x23-5B / %x5D-7E. + + error_uri + OPTIONAL. A URI identifying a human-readable web page with + information about the error, used to provide the client + developer with additional information about the error. + Values for the "error_uri" parameter MUST conform to the + URI-reference syntax and thus MUST NOT include characters + outside the set %x21 / %x23-5B / %x5D-7E. + + The parameters are included in the entity-body of the HTTP response + using the "application/json" media type as defined by [RFC4627]. The + parameters are serialized into a JSON structure by adding each + parameter at the highest structure level. Parameter names and string + values are included as JSON strings. Numerical values are included + as JSON numbers. The order of parameters does not matter and can + vary. + + For example: + + HTTP/1.1 400 Bad Request + Content-Type: application/json;charset=UTF-8 + Cache-Control: no-store + Pragma: no-cache + + { + "error":"invalid_request" + } +
    * */ public class ErrorResponse extends Response { /** * Instantiates a new error response. + * The JSON representation of an error response follows: +
    + {
    +   "error":"Unless specified otherwise, see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol",
    +   "error_description":"Unless specified otherwise, see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol", (OPTIONAL)
    +   "status_code" : the HTTP status code (would be 400 for Oauth 2.0 errors).
    + }
    + 
    * - * @param code the code - * @param message the message + * @param code HTTP status code + * @param error the error + * @param description the description (optional) */ - public ErrorResponse(int code,String message) { + public ErrorResponse(int code,String error,String description) { super(); - - JsonObject body = new JsonObject(); - if (message != null) body.add("body", new JsonPrimitive(message)); - body.add("code", new JsonPrimitive(code)); - json.add("error", body); - } + if (error == null || description == null) throw new IllegalArgumentException("One or more parameters are null"); - public ErrorResponse(JsonObject jsonMessage) { - json = jsonMessage; + json.add("error", new JsonPrimitive(error)); + json.add("status_code", new JsonPrimitive(code)); + json.add("error_description", new JsonPrimitive(description)); } /** - * Gets the error code. + * Gets the HTTP status code. * - * @return the error code + * @return the HTTP status code */ - public int getErrorCode() { - return json.get("error").getAsJsonObject().get("code").getAsInt(); + public int getStatusCode() { + return json.get("status_code").getAsInt(); } /** - * Gets the error message. + * Gets the error. * - * @return the error message + * @return the error string */ - public String getErrorMessage() { - try { - return json.get("error").getAsJsonObject().get("body").getAsString(); - } - catch(Exception e) { - return "Failed to parse error message body"; - } + public String getError() { + return json.get("error").getAsString(); + } + + /** + * Gets the error description. + * + * @return the error string + */ + public String getErrorDescription() { + return json.get("error_description").getAsString(); + } + + public boolean isTokenExpiredError() { + return getError().equals("invalid_grant"); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java index 78fa7ed3..2ad5b12d 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/JWTResponse.java @@ -43,10 +43,10 @@ public class JWTResponse extends Response { * the access token * @param token_type * the token type - * @param expiring + * @param expiresIn * the expiring */ - public JWTResponse(String access_token, String token_type, long expiring) { + public JWTResponse(String access_token, String token_type, long expiresIn) { super(); JsonObject jwt = new JsonObject(); @@ -54,12 +54,14 @@ public JWTResponse(String access_token, String token_type, long expiring) { jwt.add("access_token", new JsonPrimitive(access_token)); if (token_type != null) jwt.add("token_type", new JsonPrimitive(token_type)); - if (expiring > 0) - jwt.add("expires_in", new JsonPrimitive(expiring)); + if (expiresIn > 0) + jwt.add("expires_in", new JsonPrimitive(expiresIn)); + else + jwt.add("expires_in", new JsonPrimitive(0)); json.add("token", jwt); } - + /** * Gets the access token. * diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java index 7aa54d00..e67a9305 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/response/Response.java @@ -29,28 +29,28 @@ public abstract class Response { protected JsonObject json; public boolean isError() { - return this.getClass().equals(ErrorResponse.class); + return this instanceof ErrorResponse; } public boolean isJWTResponse() { - return this.getClass().equals(JWTResponse.class); + return this instanceof JWTResponse; } public boolean isNotification() { - return this.getClass().equals(Notification.class); + return this instanceof Notification; } public boolean isQueryResponse() { - return this.getClass().equals(QueryResponse.class); + return (this instanceof QueryResponse); } public boolean isRegistrationResponse() { - return this.getClass().equals(RegistrationResponse.class); + return this instanceof RegistrationResponse; } public boolean isSubscribeResponse() { - return this.getClass().equals(SubscribeResponse.class); + return this instanceof SubscribeResponse; } public boolean isUnsubscribeResponse() { - return this.getClass().equals(UnsubscribeResponse.class); + return this instanceof UnsubscribeResponse; } public boolean isUpdateResponse() { - return this.getClass().equals(UpdateResponse.class); + return this instanceof UpdateResponse; } /** diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java index 9a0dc134..1bf03c28 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java @@ -1,7 +1,6 @@ package it.unibo.arces.wot.sepa.commons.security; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -17,15 +16,10 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.response.JWTResponse; /** - * The Class SPARQL11Properties includes all the properties needed to connect to - * a SPARQL 1.1 Protocol Service: the URLs used by queries and updates (scheme, - * host, port and path), the HTTP method used by the primitives (GET, POST or - * URL_ENCODED_POST) and the format of the results (JSON, XML, HTML, CSV). The - * update result format is implementation specific. While for the query the - * "formats" is the required return format, for the update it specifies the - * format implemented by the SPARQL 1.1 Protocol service. +The set of properties used for client authentication * * *
    @@ -48,18 +42,14 @@ public class AuthenticationProperties {
     	private static final Logger logger = LogManager.getLogger();
     
     	/** The properties file. */
    -	protected File propertiesFile = null;
    +	protected final File propertiesFile;
     
    -	private Encryption encryption = null;
    +	private final Encryption encryption;
     
    -	protected JsonObject jsap = null;
    +	protected final JsonObject jsap;
     
    -	private String registrationURL = null;
    -	private String tokenRequestURL = null;
    -	
    -	public AuthenticationProperties() {
    -		
    -	}
    +	private final String registrationURL;
    +	private final String tokenRequestURL;
     	
     	public AuthenticationProperties(String jsapFileName, byte[] secret) throws SEPAPropertiesException {
     		FileReader in;
    @@ -68,22 +58,21 @@ public AuthenticationProperties(String jsapFileName, byte[] secret) throws SEPAP
     
     		try {
     			in = new FileReader(propertiesFile);
    -		} catch (FileNotFoundException e) {
    -			throw new SEPAPropertiesException(e);
    -		}
    -
    -		try {
     			jsap = new JsonParser().parse(in).getAsJsonObject();
    +			
    +			if (secret != null)
    +				encryption = new Encryption(secret);
    +			else
    +				encryption = new Encryption();
    +			
    +			jsap.get("oauth").getAsJsonObject().get("enable").getAsBoolean();
    +			
    +			registrationURL = jsap.get("oauth").getAsJsonObject().get("register").getAsString();
    +			tokenRequestURL = jsap.get("oauth").getAsJsonObject().get("tokenRequest").getAsString();
    +			
     		} catch (Exception e) {
     			throw new SEPAPropertiesException(e);
     		}
    -
    -		if (secret != null)
    -			encryption = new Encryption(secret);
    -		else
    -			encryption = new Encryption();
    -
    -		validate();
     	}
     
     	public AuthenticationProperties(String jsapFileName) throws SEPAPropertiesException, SEPASecurityException {
    @@ -98,42 +87,20 @@ public boolean isEnabled() {
     			return false;
     		}	
     	}
    -	
    -	private void validate() throws SEPAPropertiesException {
    -		try {
    -			jsap.get("oauth").getAsJsonObject().get("enable").getAsBoolean();
    -		}
    -		catch(Exception e) {
    -			throw new SEPAPropertiesException(new IllegalArgumentException("enable member not found"));
    -		}
    -		try {
    -			registrationURL = jsap.get("oauth").getAsJsonObject().get("register").getAsString();
    -		}
    -		catch(Exception e) {
    -			throw new SEPAPropertiesException(new IllegalArgumentException("register member not found"));
    -		}
    -		try {
    -			tokenRequestURL = jsap.get("oauth").getAsJsonObject().get("tokenRequest").getAsString();
    -		}
    -		catch(Exception e) {
    -			throw new SEPAPropertiesException(new IllegalArgumentException("tokenRequest member not found"));
    -		}
    -		
    -	}
     
     	public String getRegisterUrl() {
     		return registrationURL;
     	}
     
    -	public String getTokenRequestUrl() throws SEPAPropertiesException {
    +	public String getTokenRequestUrl()  {
     		return tokenRequestURL;
     	}
     
    -	private String getSecurityEncryptedValue(String value) throws SEPASecurityException {
    +	private String getSecurityEncryptedValue(String value) {
     		try {
     			return jsap.get("oauth").getAsJsonObject().get(value).getAsString();
     		} catch (Exception e) {
    -			throw new SEPASecurityException(e);
    +			return null;
     		}
     	}
     
    @@ -144,12 +111,8 @@ private String getSecurityEncryptedValue(String value) throws SEPASecurityExcept
     	 * @throws SEPASecurityException
     	 * @throws NumberFormatException
     	 */
    -	public boolean isTokenExpired() {
    -		try {
    -			return (new Date().getTime() >= Long.decode(encryption.decrypt(getSecurityEncryptedValue("expires"))));
    -		} catch (Exception e) {
    -			return true;
    -		}
    +	public synchronized boolean isTokenExpired() {
    +		return getExpiringTime() == 0;
     	}
     
     	/**
    @@ -159,10 +122,15 @@ public boolean isTokenExpired() {
     	 * @throws SEPASecurityException
     	 * @throws NumberFormatException
     	 */
    -	public long getExpiringSeconds() {
    +	public synchronized long getExpiringTime() {
     		try {
    -			return ((Long.decode(encryption.decrypt(getSecurityEncryptedValue("expires"))) - new Date().getTime())
    -					/ 1000);
    +			long expires = Long.decode(encryption.decrypt(getSecurityEncryptedValue("expires")));
    +			long now = new Date().getTime();
    +			
    +			logger.debug("@getExpiringTime Diff:"+(expires-now)+" Now: "+now+" Expires: "+expires);
    +			
    +			if (expires-now < 0) return 0;
    +			return expires-now;
     		} catch (Exception e) {
     			return 0;
     		}
    @@ -174,16 +142,21 @@ public long getExpiringSeconds() {
     	 * @return the access token
     	 * @throws SEPASecurityException
     	 */
    -	public String getBearerAuthorizationHeader() throws SEPASecurityException {
    +	public synchronized String getBearerAuthorizationHeader() {
     		try {
     			return "Bearer " + encryption.decrypt(getSecurityEncryptedValue("jwt"));
     		} catch (Exception e) {
    -			throw new SEPASecurityException(e);
     		}
    +		
    +		return null;
     	}
     	
    -	public String getToken() throws SEPASecurityException {
    -		return encryption.decrypt(getSecurityEncryptedValue("jwt"));
    +	public synchronized String getToken() {
    +		try {
    +			return encryption.decrypt(getSecurityEncryptedValue("jwt"));
    +		} catch (SEPASecurityException e) {
    +			return null;
    +		}
     	}
     
     	/**
    @@ -192,11 +165,11 @@ public String getToken() throws SEPASecurityException {
     	 * @return the token type
     	 * @throws SEPASecurityException
     	 */
    -	public String getTokenType() throws SEPASecurityException {
    +	public synchronized String getTokenType() {
     		try {
     			return encryption.decrypt(getSecurityEncryptedValue("type"));
     		} catch (Exception e) {
    -			throw new SEPASecurityException(e);
    +			return null;
     		}
     	}
     
    @@ -206,17 +179,16 @@ public String getTokenType() throws SEPASecurityException {
     	 * @return the basic authorization
     	 * @throws SEPASecurityException
     	 */
    -	public String getBasicAuthorizationHeader() throws SEPASecurityException {
    +	public synchronized String getBasicAuthorizationHeader() {
     		try {
     			return "Basic " + new String(Base64.getEncoder().encode(
    -					(encryption.decrypt(jsap.get("oauth").getAsJsonObject().get("client_id").getAsString())
    +					(encryption.decrypt(getSecurityEncryptedValue("client_id"))
     							+ ":"
    -							+ encryption.decrypt(
    -									jsap.get("oauth").getAsJsonObject().get("client_secret").getAsString()))
    +							+ encryption.decrypt(getSecurityEncryptedValue("client_secret")))
     											.getBytes("UTF-8")),
     					"UTF-8");
     		} catch (Exception e) {
    -			throw new SEPASecurityException(e);
    +			return null;	
     		}
     	}
     
    @@ -230,8 +202,8 @@ public String getBasicAuthorizationHeader() throws SEPASecurityException {
     	 * @throws SEPASecurityException
     	 * @throws SEPAPropertiesException
     	 */
    -	public void setCredentials(String id, String secret) throws SEPASecurityException, SEPAPropertiesException {
    -		logger.debug("Set credentials Id: " + id + " Secret:" + secret);
    +	public synchronized void setCredentials(String id, String secret) throws SEPAPropertiesException, SEPASecurityException {
    +		logger.debug("@setCredentials Id: " + id + " Secret:" + secret);
     
     		// Save on file the encrypted version
     		if (!jsap.has("oauth")) {
    @@ -261,40 +233,27 @@ public void setCredentials(String id, String secret) throws SEPASecurityExceptio
     	 * @throws SEPASecurityException
     	 *
     	 */
    -	public void setJWT(String jwt, Date expires, String type) throws SEPASecurityException, SEPAPropertiesException {
    -
    +	public void setJWT(JWTResponse jwt) throws SEPASecurityException, SEPAPropertiesException {
    +		logger.debug("@setJWT: "+jwt);
    +		
    +		long expires = new Date().getTime() + 1000 * jwt.getExpiresIn();
    +		
     		// Save on file the encrypted version
     		if (!jsap.has("oauth")) {
     			JsonObject credentials = new JsonObject();
    -			credentials.add("jwt", new JsonPrimitive(encryption.encrypt(jwt)));
    -			credentials.add("expires", new JsonPrimitive(encryption.encrypt(String.format("%d", expires.getTime()))));
    -			credentials.add("type", new JsonPrimitive(encryption.encrypt(type)));
    +			credentials.add("jwt", new JsonPrimitive(encryption.encrypt(jwt.getAccessToken())));
    +			credentials.add("expires", new JsonPrimitive(encryption.encrypt(String.format("%d", expires))));
    +			credentials.add("type", new JsonPrimitive(encryption.encrypt(jwt.getTokenType())));
     			jsap.add("oauth", credentials);
     		} else {
    -			jsap.get("oauth").getAsJsonObject().add("jwt", new JsonPrimitive(encryption.encrypt(jwt)));
    +			jsap.get("oauth").getAsJsonObject().add("jwt", new JsonPrimitive(encryption.encrypt(jwt.getAccessToken())));
     			jsap.get("oauth").getAsJsonObject().add("expires",
    -					new JsonPrimitive(encryption.encrypt(String.format("%d", expires.getTime()))));
    -			jsap.get("oauth").getAsJsonObject().add("type", new JsonPrimitive(encryption.encrypt(type)));
    +					new JsonPrimitive(encryption.encrypt(String.format("%d", expires))));
    +			jsap.get("oauth").getAsJsonObject().add("type", new JsonPrimitive(encryption.encrypt(jwt.getTokenType())));
     		}
     
     		storeProperties(propertiesFile.getAbsolutePath());
    -	}
    -	
    -	/**
    -	 * Sets the JWT.
    -	 *
    -	 * @param jwt
    -	 *            the JSON Web Token
    -	 * @param expiresIn
    -	 *            the number of seconds from now to when the token will expire
    -	 * @param type
    -	 *            the token type (e.g., bearer)
    -	 * @throws SEPAPropertiesException
    -	 * @throws SEPASecurityException
    -	 *
    -	 */
    -	public void setJWT(String accessToken, long expiresIn, String tokenType) throws SEPASecurityException, SEPAPropertiesException {
    -		setJWT(accessToken,new Date(new Date().getTime() + expiresIn),tokenType);
    +		
     	}
     
     	/**
    @@ -316,5 +275,5 @@ protected void storeProperties(String propertiesFile) throws SEPAPropertiesExcep
     		} catch (IOException e) {
     			throw new SEPAPropertiesException(e);
     		}
    -	}	
    +	}
     }
    diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java
    index e5b08eef..94c7a1ea 100644
    --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java
    +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java
    @@ -30,6 +30,7 @@
     import java.security.NoSuchAlgorithmException;
     import java.security.UnrecoverableKeyException;
     import java.security.cert.CertificateException;
    +import java.util.Date;
     
     import javax.net.ssl.HostnameVerifier;
     import javax.net.ssl.KeyManagerFactory;
    @@ -140,23 +141,42 @@
      * protocols:
      * 
      * SSLv3 TLSv1 TLSv1.1 TLSv1.2
    + * 
      * 
    + * + * @see HostnameVerifier */ public class SEPASecurityManager implements HostnameVerifier { - /** The JAVA key store. */ - KeyStore keystore; + /** + * The JAVA key store. + * + * @see KeyStore + */ + private final KeyStore keystore; - /** The ssl context. */ - SSLContext sslContext; + /** + * The SSL context. + * + * @see SSLContext + */ + //private final SSLContext sslContext; - SSLConnectionSocketFactory sslsf; + /** + * The SSLConnectionSocketFactory context. + * + * @see SSLConnectionSocketFactory + */ + private final SSLConnectionSocketFactory sslsf; /** The log4j2 logger. */ private static final Logger logger = LogManager.getLogger(); - private AuthenticationProperties oauthProperties = null; + private final AuthenticationProperties oauthProperties; + private final KeyManagerFactory kmfactory; + private final TrustManagerFactory tmf; + /** * Instantiates a new Security Manager. * @@ -185,15 +205,12 @@ public SEPASecurityManager(String jksName, String jksPassword, String keyPasswor keystore = KeyStore.getInstance("JKS"); keystore.load(new FileInputStream(jksName), jksPassword.toCharArray()); - KeyManagerFactory kmfactory = KeyManagerFactory.getInstance("SunX509"); + kmfactory = KeyManagerFactory.getInstance("SunX509"); kmfactory.init(keystore, keyPassword.toCharArray()); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf = TrustManagerFactory.getInstance("SunX509"); tmf.init(keystore); - sslContext = SSLContext.getInstance("TLSv1"); - sslContext.init(kmfactory.getKeyManagers(), tmf.getTrustManagers(), null); - // Trust own CA and all self-signed certificates and allow TLSv1 protocol only sslsf = new SSLConnectionSocketFactory(SSLContexts.custom() .loadTrustMaterial(new File(jksName), jksPassword.toCharArray(), new TrustSelfSignedStrategy()) @@ -209,34 +226,50 @@ public SEPASecurityManager(String jksName, String jksPassword, String keyPasswor public SEPASecurityManager(AuthenticationProperties oauthProp) throws SEPASecurityException { this("sepa.jks", "sepa2017", "sepa2017", oauthProp); } - + public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { - this(jksName,jksPassword,keyPassword, new AuthenticationProperties()); - } -// if (oauthProp == null) -// throw new IllegalArgumentException("Authorization properties are null"); - -// public SEPASecurityManager() throws SEPASecurityException { -// _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); -// } -// -// public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { -// _SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", null); -// } - -// public SEPASecurityManager(String jksName, String jksPassword, String keyPassword, -// AuthenticationProperties oauthProp) throws SEPASecurityException { -// _SEPASecurityManager(jksName, "sepa2017", "sepa2017", oauthProp); -// -// if (oauthProp == null) -// throw new IllegalArgumentException("Authorization properties are null"); -// } - - public Socket getSSLSocket() throws IOException { - return sslContext.getSocketFactory().createSocket(); + this(jksName, jksPassword, keyPassword, null); + } + + public SEPASecurityManager() throws SEPASecurityException { + this("sepa.jks", "sepa2017", "sepa2017", null); + } + + public Socket getSSLSocket() throws SEPASecurityException { + SSLContext sslContext; + + try { + sslContext = SSLContext.getInstance("TLSv1"); + } catch (NoSuchAlgorithmException e) { + throw new SEPASecurityException(e); + } + try { + sslContext.init(kmfactory.getKeyManagers(), tmf.getTrustManagers(), null); + } catch (KeyManagementException e) { + throw new SEPASecurityException(e); + } + + try { + return sslContext.getSocketFactory().createSocket(); + } catch (IOException e) { + throw new SEPASecurityException(e); + } } - public SSLContext getSSLContext() { + public SSLContext getSSLContext() throws SEPASecurityException { + SSLContext sslContext; + + try { + sslContext = SSLContext.getInstance("TLSv1"); + } catch (NoSuchAlgorithmException e) { + throw new SEPASecurityException(e); + } + try { + sslContext.init(kmfactory.getKeyManagers(), tmf.getTrustManagers(), null); + } catch (KeyManagementException e) { + throw new SEPASecurityException(e); + } + return sslContext; } @@ -255,44 +288,94 @@ public boolean verify(String hostname, SSLSession session) { return true; } - /** - * Register the identity and store the credentials into the Authentication properties + /** + * Register the identity and store the credentials into the Authentication + * properties + * + * @param identity + * is a string that identifies the client (e.g., registration code, + * MAC address, EPC, ...) + * @return RegistrationResponse or ErrorResponse in case of an error + * @see RegistrationResponse + * @see ErrorResponse + * @throws SEPAPropertiesException + * @throws SEPASecurityException + */ + public synchronized Response register(String identity) throws SEPASecurityException, SEPAPropertiesException { + if (oauthProperties == null) + throw new SEPAPropertiesException("Authorization properties are null"); + + Response ret = register(oauthProperties.getRegisterUrl(), identity); + + if (ret.isRegistrationResponse()) { + RegistrationResponse reg = (RegistrationResponse) ret; + oauthProperties.setCredentials(reg.getClientId(), reg.getClientSecret()); + } + else { + logger.error(ret); + } + + return ret; + } + + /** + * Returns the Bearer authentication header * @throws SEPAPropertiesException * @throws SEPASecurityException - */ - public Response register(String identity) throws SEPASecurityException, SEPAPropertiesException { - Response ret = register(oauthProperties.getRegisterUrl(), identity); - - if (ret.isError()) return ret; + * + * @see AuthenticationProperties + */ + public synchronized String getAuthorizationHeader() throws SEPASecurityException, SEPAPropertiesException { + if (oauthProperties == null) return null; - RegistrationResponse reg = (RegistrationResponse) ret; - oauthProperties.setCredentials(reg.getClientId(), reg.getClientSecret()); + if(isTokenExpired()) { + requestToken(); + } - return ret; + return oauthProperties.getBearerAuthorizationHeader(); } - - // Get the authorization header (by requesting a new token first) - public String getAuthorizationHeader() throws SEPAPropertiesException, SEPASecurityException { - Response ret = requestToken(oauthProperties.getTokenRequestUrl(), - oauthProperties.getBasicAuthorizationHeader()); - - if (ret.isError()) { - if (((ErrorResponse) ret).getErrorCode() != 400) - throw new SEPASecurityException(new IllegalArgumentException("Failed to get a new token")); - return oauthProperties.getBearerAuthorizationHeader(); + + /** + * It is used to request a new token using the "Basic" credentials stored in the AuthenticationProperties. + * When retrieved, the token is stored within the AuthenticationProperties. + * + * @return In case of success, it returns an JWTResponse. Otherwise an ErrorResponse is returned as specified in RFC6749 + * @throws SEPASecurityException + * @throws SEPAPropertiesException + * @see ErrorResponse + * @see JWTResponse + * @see AuthenticationProperties + */ + private void requestToken() throws SEPASecurityException, SEPAPropertiesException { + Response ret = requestToken(oauthProperties.getTokenRequestUrl(),oauthProperties.getBasicAuthorizationHeader()); + + if (ret.isJWTResponse()) { + JWTResponse jwt = (JWTResponse) ret; + + logger.debug(jwt); + + oauthProperties.setJWT(jwt); } + else { + logger.error("requestToken@ "+new Date()+" Response: "+ret); + } + } - JWTResponse token = (JWTResponse) ret; - oauthProperties.setJWT(token.getAccessToken(), token.getExpiresIn(), token.getTokenType()); - - return oauthProperties.getBearerAuthorizationHeader(); + /** + * Returns true if the token is expired or not available. If the token is + * expired, the client MUST request a new token to renew the authorization + * header. + * + * @throws SEPAPropertiesException + * + * @see AuthenticationProperties + */ + private boolean isTokenExpired() { + return oauthProperties.isTokenExpired(); } - /** - * Request registration of "entity" at the Authorization Server listening at the specified URL - * */ private Response register(String url, String identity) { - logger.debug("REGISTER " + identity); + logger.info("REGISTER " + identity); CloseableHttpResponse response = null; long start = Timings.getTime(); @@ -317,22 +400,25 @@ private Response register(String url, String identity) { if (json.has("error")) { Timings.log("REGISTER_ERROR", start, Timings.getTime()); - ErrorResponse error = new ErrorResponse(json.get("error").getAsJsonObject().get("code").getAsInt(), - json.get("error").getAsJsonObject().get("body").getAsString()); - logger.error(error); - return error; + int code = json.get("status_code").getAsInt(); + String error = json.get("error").getAsString(); + String description = json.get("error_description").getAsString(); + + ErrorResponse ret = new ErrorResponse(code, error, description); + logger.error(ret); + return ret; } String id = json.get("credentials").getAsJsonObject().get("client_id").getAsString(); String secret = json.get("credentials").getAsJsonObject().get("client_secret").getAsString(); JsonElement signature = json.get("credentials").getAsJsonObject().get("signature"); - Timings.log("REGISTER", start,Timings.getTime()); + Timings.log("REGISTER", start, Timings.getTime()); return new RegistrationResponse(id, secret, signature); } catch (Exception e) { logger.error(e.getMessage()); Timings.log("REGISTER_ERROR", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Exception", e.getMessage()); } finally { try { if (response != null) @@ -340,29 +426,13 @@ private Response register(String url, String identity) { } catch (IOException e) { logger.error(e.getMessage()); Timings.log("REGISTER_ERROR", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "IOException", e.getMessage()); } } } - /** - * It is used to request a new token. - * - * @param url - * the url of the Authorization Server - * @param authorization - * the header to be sent (e.g., "Basic authorization header") - * - * @return ErrorResponse in case of error. The error codes correspond to an HTTP - * status codes as follows: SC_UNAUTHORIZED (401) : client not - * authorized, SC_BAD_REQUEST (400) : the token is not expired, - * SC_INTERNAL_SERVER_ERROR (500) : error validating the token - * @see ErrorResponse - * @see JWTResponse - */ - private Response requestToken(String url, String authorization) { - logger.debug("TOKEN_REQUEST"); + logger.info("TOKEN_REQUEST: "+authorization); CloseableHttpResponse response = null; long start = Timings.getTime(); @@ -387,15 +457,8 @@ private Response requestToken(String url, String authorization) { if (json.has("error")) { Timings.log("TOKEN_REQUEST", start, Timings.getTime()); - - //TOKEN IS NOT EXPIRED - if (json.get("error").getAsJsonObject().get("code").getAsInt()==400 && oauthProperties != null) { - return new JWTResponse(oauthProperties.getToken(),oauthProperties.getTokenType(),oauthProperties.getExpiringSeconds()); - } - - ErrorResponse error = new ErrorResponse(json.get("error").getAsJsonObject().get("code").getAsInt(), - json.get("error").getAsJsonObject().get("body").getAsString()); - logger.error(error); + ErrorResponse error = new ErrorResponse(json.get("status_code").getAsInt(), + json.get("error").getAsString(), json.get("error_description").getAsString()); return error; } @@ -407,7 +470,7 @@ private Response requestToken(String url, String authorization) { } catch (Exception e) { logger.error(e.getMessage()); Timings.log("TOKEN_REQUEST", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Exception", e.getMessage()); } finally { try { if (response != null) @@ -415,7 +478,7 @@ private Response requestToken(String url, String authorization) { } catch (IOException e) { logger.error(e.getMessage()); Timings.log("TOKEN_REQUEST", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "IOException", e.getMessage()); } } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java index 565d229c..9ab0c99f 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java @@ -28,8 +28,8 @@ import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; import it.unibo.arces.wot.sepa.commons.sparql.RDFTerm; import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; -import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java index 4cbf1269..0c8d1a27 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java @@ -24,8 +24,8 @@ import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; -import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; @@ -100,7 +100,12 @@ public void unsubscribe(String subID,long timeout) throws SEPASecurityException, String clientURL = subscriptions.get(subID); String auth = null; - if (subscribedClients.get(clientURL).isSecure()) auth = sm.getAuthorizationHeader(); + try { +// auth = appProfile.getAuthenticationProperties().getBearerAuthorizationHeader(); + auth = sm.getAuthorizationHeader(); + } catch (Exception e) { + } + //if (subscribedClients.get(clientURL).isSecure()) auth = sm.getAuthorizationHeader(); subscribedClients.get(clientURL).unsubscribe(new UnsubscribeRequest(subID, auth,timeout)); @@ -206,7 +211,7 @@ private void _subscribe(String ID, String sparql, Bindings forced, ISubscription protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID), sm,handler); - client = new SPARQL11SEProtocol(protocol, sm); + client = new SPARQL11SEProtocol(protocol); } else client = subscribedClients.get(url); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java index 8123c5b2..7389875b 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/timing/Timings.java @@ -15,7 +15,7 @@ public static long getTime() { } public synchronized static void log(String tag,long start,long stop) { - String message = String.format("%d,%s,%d",System.currentTimeMillis(),tag,stop-start); + String message = String.format("%d,%d,%s",System.currentTimeMillis(),stop-start,tag); logger.log(Level.getLevel("timing"),message); } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index 03a8ffa9..29c59906 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -14,7 +14,7 @@ public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEP if( configuaration != null){ result = new JSAP(configuaration); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest-secure.jsap"); result = new JSAP(config.getPath()); } return result; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index e98e2da9..4e9cb76b 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -1,16 +1,12 @@ package it.unibo.arces.wot.sepa.api; -import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; +import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; @@ -21,7 +17,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -29,24 +24,22 @@ import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicLong; import static org.junit.Assert.*; -public class ITSPARQL11SEProtocol implements ISubscriptionHandler { - protected final Logger logger = LogManager.getLogger(); +public class ITSPARQL11SEProtocol { + protected final static Logger logger = LogManager.getLogger(); protected static JSAP properties = null; private static SEPASecurityManager sm; - private static SPARQL11SEProtocol client = null; + private static SPARQL11Protocol client; private final static String VALID_ID = "SEPATest"; private final static String NOT_VALID_ID = "RegisterMePlease"; - private static AtomicLong events = new AtomicLong(0); - private static AtomicLong subscribes = new AtomicLong(0); - private String spuid = null; - private static final Object spuidMutex = new Object(); + private final static Sync sync = new Sync(); + private final ArrayList subscribers = new ArrayList(); + private final ArrayList publishers = new ArrayList(); @BeforeClass public static void init() throws Exception { @@ -62,41 +55,44 @@ public static void init() throws Exception { } } - @AfterClass - public static void dispose() throws IOException { - - } - @Before public void beginTest() throws IOException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, URISyntaxException { - SubscriptionProtocol protocol; + sync.reset(); - if (sm == null) { - protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), - properties.getSubscribePath(), this); - client = new SPARQL11SEProtocol(protocol); - } else { - protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), - properties.getSubscribePath(), sm, this); - client = new SPARQL11SEProtocol(protocol, sm); - } + if (sm != null) + client = new SPARQL11Protocol(sm); + else + client = new SPARQL11Protocol(); - assertFalse("Failed to create SPARQL11SEProtocol", client == null); + subscribers.clear(); + publishers.clear(); Response ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); - assertFalse("Failed to create Delete all triples", ret.isError()); + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + } - subscribes.set(0); - events.set(0); + } + assertFalse(String.valueOf(ret), ret.isError()); } @After - public void endTest() throws IOException { + public void endTest() throws IOException, InterruptedException { if (client != null) client.close(); + + for (Subscriber sub : subscribers) + sub.close(); + for (Publisher pub : publishers) { + pub.finish(); + pub.interrupt(); + pub.join(); + } } @Test(timeout = 5000) @@ -116,490 +112,354 @@ public void Register() throws SEPASecurityException, SEPAPropertiesException { } @Test(timeout = 5000) - public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException { + public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { // Delete all triples Response ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + } + + } assertFalse(String.valueOf(ret), ret.isError()); - // Check that the store size is 0 + // Evaluate if the store is empty ret = client.query(buildQueryRequest("COUNT", 5000)); + + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + ret = client.query(buildQueryRequest("COUNT", 5000)); + } + + } assertFalse(String.valueOf(ret), ret.isError()); QueryResponse results = (QueryResponse) ret; assertFalse(String.valueOf(results), results.getBindingsResults().size() != 1); for (Bindings bindings : results.getBindingsResults().getBindings()) { - assertFalse(String.valueOf(results), bindings.getValue("n") == null); - assertFalse(String.valueOf(results), !bindings.getValue("n").equals("0")); + assertFalse("Results are null " + String.valueOf(results), bindings.getValue("n") == null); + assertFalse("RDF store is not empty " + String.valueOf(results), !bindings.getValue("n").equals("0")); } } - @Test(timeout = 15000) - public void GetAuthorizationHeader() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { + @Test(timeout = 25000) + public void RequestToken() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { if (sm != null) { - sm.register(VALID_ID); - sm.getAuthorizationHeader(); - - // For testing the token expires in 5 seconds - for (int i = 0; i < 10; i++) { - sm.getAuthorizationHeader(); + for (int i = 0; i < 20; i++) { + String authorization = sm.getAuthorizationHeader(); + assertFalse("Failed to get authorization header", authorization == null); Thread.sleep(1000); } } } - @Test(timeout = 1000) - public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException { + @Test(timeout = 5000) + public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + } + } assertFalse(String.valueOf(ret), ret.isError()); } - @Test(timeout = 1000) - public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException { + @Test(timeout = 5000) + public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.query(buildQueryRequest("ALL", 5000)); + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + client.query(buildQueryRequest("ALL", 5000)); + } + } assertFalse(String.valueOf(ret), ret.isError()); } - @Test(timeout = 1000) - public void UpdateAndQuery() throws IOException, SEPAPropertiesException, SEPASecurityException { + @Test(timeout = 5000) + public void UpdateAndQuery() + throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + } + } assertFalse(String.valueOf(ret), ret.isError()); ret = client.query(buildQueryRequest("VAIMEE", 5000)); + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + if (error.isTokenExpiredError()) { + client.query(buildQueryRequest("VAIMEE", 5000)); + } + } assertFalse(String.valueOf(ret), ret.isError()); assertFalse(String.valueOf(ret), ((QueryResponse) ret).getBindingsResults().size() != 1); } - @Test(timeout = 1000) - public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - client.subscribe(buildSubscribeRequest("ALL", 5000)); - - synchronized (subscribes) { - try { - subscribes.wait(); - } catch (InterruptedException e) { + @Test(timeout = 5000) + public void Subscribe() + throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { + subscribers.add(new Subscriber("ALL", properties, sm, sync)); - } - } + for (Subscriber sub : subscribers) + sub.subscribe(); - client.close(); + sync.waitSubscribes(subscribers.size()); + sync.waitEvents(subscribers.size()); - assertFalse("Failed to subscribe", subscribes.get() == 0); + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", + sync.getSubscribes() != subscribers.size()); + assertFalse("Events:" + sync.getEvents() + "(" + subscribers.size() + ")", + sync.getEvents() != subscribers.size()); } - @Test(timeout = 1000) - public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - int n = 33; - int total = 3 * n; + @Test(timeout = 60000) + public void Subscribe3xN() + throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { + int n = 30; for (int i = 0; i < n; i++) { - client.subscribe(buildSubscribeRequest("ALL", 5000)); - client.subscribe(buildSubscribeRequest("RANDOM", 5000)); - client.subscribe(buildSubscribeRequest("RANDOM1", 5000)); + subscribers.add(new Subscriber("ALL", properties, sm, sync)); + subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); + subscribers.add(new Subscriber("RANDOM1", properties, sm, sync)); } - while (subscribes.get() < total) { - synchronized (subscribes) { - try { - subscribes.wait(2000); - } catch (InterruptedException e) { - - } - } + for (Subscriber sub : subscribers) { + sub.subscribe(); + Thread.sleep(500); } - while (events.get() < total) { - synchronized (events) { - try { - events.wait(2000); - } catch (InterruptedException e) { + sync.waitSubscribes(subscribers.size()); + sync.waitEvents(subscribers.size()); - } - } - } - - client.close(); - - assertFalse("Failed to subscribe (" + subscribes.get() + " on " + total + ")", subscribes.get() != total); - assertFalse("Failed to receive first results. Received: " + events.get() + " on: " + total + " events", - events.get() != total); + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", + sync.getSubscribes() != subscribers.size()); + assertFalse("Events:" + sync.getEvents() + "(" + subscribers.size() + ")", + sync.getEvents() != subscribers.size()); } - @Test(timeout = 1000) - public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - spuid = null; - - client.subscribe(buildSubscribeRequest("ALL", 5000)); - - while (spuid == null) { - synchronized (spuidMutex) { - try { - spuidMutex.wait(1000); - } catch (InterruptedException e) { - - } - } - } - - while (events.get() < 1) { - synchronized (events) { - try { - events.wait(1000); - } catch (InterruptedException e) { - - } - } - } - - if (spuid != null) { - client.unsubscribe(buildUnsubscribeRequest(spuid, 5000)); - - while (subscribes.get() != 0) { - synchronized (subscribes) { - try { - subscribes.wait(1000); - } catch (InterruptedException e) { - - } - } - } - } - - client.close(); - - assertFalse("Failed to subscribe", spuid == null); - assertFalse("Received: " + events.get(), events.get() != 1); - assertFalse("Failed to unsubscribe " + subscribes.get(), subscribes.get() != 0); + @Test(timeout = 5000) + public void Unsubscribe() + throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { + subscribers.add(new Subscriber("ALL", properties, sm, sync)); + for (Subscriber sub : subscribers) + sub.subscribe(); + + sync.waitSubscribes(subscribers.size()); + + for (Subscriber sub : subscribers) + sub.unsubscribe(sync.getSpuid()); + + sync.waitEvents(subscribers.size()); + sync.waitUnsubscribes(subscribers.size()); + + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", + sync.getSubscribes() != subscribers.size()); + assertFalse("Events:" + sync.getEvents() + "(" + subscribers.size() + ")", + sync.getEvents() != subscribers.size()); + assertFalse("Unsubscribes:" + sync.getUnsubscribes() + "(" + subscribers.size() + ")", + sync.getUnsubscribes() != subscribers.size()); } - @Test(timeout = 1000) + @Test(timeout = 5000) public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException { - Subscriber sub = new Subscriber("VAIMEE", properties, sm, this); - sub.start(); - - while (spuid == null) { - synchronized (spuidMutex) { - try { - spuidMutex.wait(1000); - } catch (InterruptedException e) { - - } - } - } - - synchronized (events) { - while (events.get() < 1) - try { - events.wait(1000); - } catch (InterruptedException e) { + subscribers.add(new Subscriber("VAIMEE", properties, sm, sync)); + for (Subscriber sub : subscribers) + sub.subscribe(); - } - } + sync.waitSubscribes(subscribers.size()); - Publisher pub = null; - - if (spuid != null) { - pub = new Publisher("VAIMEE", properties, sm, 1); + publishers.add(new Publisher("VAIMEE", properties, sm, 1)); + for (Publisher pub : publishers) pub.start(); - synchronized (events) { - while (events.get() < 2) - try { - events.wait(1000); - } catch (InterruptedException e) { + sync.waitEvents(2); - } - } - } - - sub.finish(); - sub.interrupt(); - - if (pub != null) { - pub.interrupt(); - pub.join(1000); - } - - sub.join(1000); - - assertFalse("Failed to subscribe", spuid == null); - assertFalse("Notification not received " + events.get(), events.get() != 2); + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", + sync.getSubscribes() != subscribers.size()); + assertFalse("Events:" + sync.getEvents() + "(2)", sync.getEvents() != 2); } - @Test(timeout = 5000) + @Test(timeout = 60000) public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { int n = 10; - int total = n * n * n; - - ArrayList subscribers = new ArrayList(); - ArrayList publishers = new ArrayList(); - - Publisher pub; - Subscriber sub; for (int i = 0; i < n; i++) { - sub = new Subscriber("RANDOM", properties, sm, this); - sub.start(); - subscribers.add(sub); + subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); + publishers.add(new Publisher("RANDOM", properties, sm, n)); } - while (subscribes.get() < n) { - synchronized (subscribes) { - try { - subscribes.wait(5000); - } catch (InterruptedException e) { - - } - } - } + for (Subscriber sub : subscribers) + sub.subscribe(); - synchronized (events) { - while (events.get() < n) - try { - events.wait(5000); - } catch (InterruptedException e) { + sync.waitSubscribes(subscribers.size()); - } - } - - for (int i = 0; i < n; i++) { - pub = new Publisher("RANDOM", properties, sm, n); - publishers.add(pub); + for (Publisher pub : publishers) pub.start(); - } - for (Publisher pb1 : publishers) - pb1.join(); + sync.waitEvents(subscribers.size() + subscribers.size() * publishers.size() * publishers.size()); - synchronized (events) { - while (events.get() < total+n) - try { - events.wait(5000); - } catch (InterruptedException e) { - - } - } - - for (Subscriber sb1 : subscribers) { - sb1.finish(); - sb1.interrupt(); - sb1.join(100); - } - - assertFalse("Failed to subscribe (" + subscribes.get() + " on " + n + ")", subscribes.get() != n); - assertFalse("Received: " + events.get() + " on: " + total+n + " events", events.get() != total+n); + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", + sync.getSubscribes() != subscribers.size()); + assertFalse( + "Events:" + sync.getEvents() + "(" + subscribers.size() + + subscribers.size() * publishers.size() * publishers.size() + ")", + sync.getEvents() != subscribers.size() + subscribers.size() * publishers.size() * publishers.size()); } - @Test(timeout = 10000) + @Test(timeout = 60000) public void UpdateHeavyLoad() throws InterruptedException { int n = 10; - ArrayList pool = new ArrayList(); - Publisher pb; - for (int i = 0; i < n; i++) { - pb = new Publisher("RANDOM", properties, sm, n); - pool.add(pb); - pb.start(); - pb = new Publisher("RANDOM1", properties, sm, n); - pool.add(pb); - pb.start(); - pb = new Publisher("VAIMEE", properties, sm, n); - pool.add(pb); - pb.start(); + publishers.add(new Publisher("RANDOM", properties, sm, n)); + publishers.add(new Publisher("RANDOM1", properties, sm, n)); + publishers.add(new Publisher("VAIMEE", properties, sm, n)); } - for (Publisher pb1 : pool) - pb1.join(n*500); + for (Publisher pub : publishers) + pub.start(); + + for (Publisher pub : publishers) + pub.join(); + publishers.clear(); } - @Test(timeout = 10000) - public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, - SEPAPropertiesException, SEPASecurityException { + /* To be used for long lasting test*/ + //@Test + public void StressTest() throws IOException, IllegalArgumentException, SEPAProtocolException, + InterruptedException, SEPAPropertiesException, SEPASecurityException { int n = 10; - ArrayList subscribers = new ArrayList(); - ArrayList publishers = new ArrayList(); - - Publisher pub; - Subscriber sub; - - int total = 4 * n * n * n; - for (int i = 0; i < n; i++) { - sub = new Subscriber("ALL", properties, sm, this); - sub.start(); - subscribers.add(sub); - - sub =new Subscriber("RANDOM", properties, sm, this); - sub.start(); - subscribers.add(sub); - - sub = new Subscriber("RANDOM1", properties, sm, this); - sub.start(); - subscribers.add(sub); + subscribers.add(new Subscriber("ALL", properties, sm, sync)); + subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); + subscribers.add(new Subscriber("RANDOM1", properties, sm, sync)); } - while (subscribes.get() < 3 * n) { - synchronized (subscribes) { - try { - subscribes.wait(10000); - } catch (InterruptedException e) { - - } + new Thread() { + public void run() { + for (Subscriber sub : subscribers) + try { + sub.subscribe(); + } catch (SEPAProtocolException | InterruptedException e) { + logger.error(e.getMessage()); + } } - } - synchronized (events) { - while (events.get() < 3 * n) - try { - events.wait(5000); - } catch (InterruptedException e) { + }.start(); - } - } + sync.waitSubscribes(subscribers.size()); + sync.waitEvents(subscribers.size()); + + assertFalse("Events:" + sync.getEvents() + "(" + subscribers.size() + ")", sync.getEvents() != subscribers.size()); + + int events = 4 * n * n * n; + + while (true) { + publishers.clear(); + sync.reset(); + + for (int i = 0; i < n; i++) { + publishers.add(new Publisher("RANDOM", properties, sm, n)); + publishers.add(new Publisher("RANDOM1", properties, sm, n)); + } + + for (Publisher pub : publishers) + pub.start(); - for (int i = 0; i < n; i++) { - pub = new Publisher("RANDOM", properties, sm, n); - publishers.add(pub); - pub.start(); + for (Publisher pub : publishers) + pub.join(); - pub = new Publisher("RANDOM1", properties, sm, n); - publishers.add(pub); - pub.start(); + sync.waitEvents(events); + + assertFalse("Events:" + sync.getEvents() + "(" + events + ")", sync.getEvents() != events); } + } - for (Publisher pb1 : publishers) - pb1.join(n*500); + @Test(timeout = 60000) + public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, + SEPAPropertiesException, SEPASecurityException { + int n = 10; - synchronized (events) { - while (events.get() < total+ 3*n) - try { - events.wait(5000); - } catch (InterruptedException e) { + for (int i = 0; i < n; i++) { + subscribers.add(new Subscriber("ALL", properties, sm, sync)); + subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); + subscribers.add(new Subscriber("RANDOM1", properties, sm, sync)); - } - } - - for (Subscriber sb1 : subscribers) { - sb1.finish(); - sb1.interrupt(); - sb1.join(100); + publishers.add(new Publisher("RANDOM", properties, sm, n)); + publishers.add(new Publisher("RANDOM1", properties, sm, n)); } - assertFalse("Failed to subscribe (" + subscribes.get() + " on " + 3 * n + ")", subscribes.get() != 3 * n); - assertFalse("Events not received (" + events.get() + " on " + total + 3*n + ")", events.get() != total+3*n); - } + int events = 4 * n * n * n + subscribers.size(); - protected static UpdateRequest buildUpdateRequest(String id, long timeout) - throws SEPAPropertiesException, SEPASecurityException { - HTTPMethod method = properties.getUpdateMethod(id); - String scheme = properties.getUpdateProtocolScheme(id); - String host = properties.getUpdateHost(id); - int port = properties.getUpdatePort(id); - String path = properties.getUpdatePath(id); - String sparql = properties.getSPARQLUpdate(id); - String graphUri = properties.getUsingGraphURI(id); - String namedGraphUri = properties.getUsingNamedGraphURI(id); + new Thread() { + public void run() { + for (Subscriber sub : subscribers) + try { + sub.subscribe(); + } catch (SEPAProtocolException | InterruptedException e) { + logger.error(e.getMessage()); + } + } - String authorization = null; - if (sm != null) - authorization = sm.getAuthorizationHeader(); + }.start(); - return new UpdateRequest(method, scheme, host, port, path, sparql, graphUri, namedGraphUri, authorization, - timeout); - } + sync.waitSubscribes(subscribers.size()); - protected static QueryRequest buildQueryRequest(String id, long timeout) - throws SEPAPropertiesException, SEPASecurityException { - HTTPMethod method = properties.getQueryMethod(id); - String scheme = properties.getQueryProtocolScheme(id); - String host = properties.getQueryHost(id); - int port = properties.getQueryPort(id); - String path = properties.getQueryPath(id); - String sparql = properties.getSPARQLQuery(id); - String graphUri = properties.getDefaultGraphURI(id); - String namedGraphUri = properties.getNamedGraphURI(id); + for (Publisher pub : publishers) + pub.start(); - String authorization = null; - if (sm != null) - authorization = sm.getAuthorizationHeader(); + sync.waitEvents(events); - return new QueryRequest(method, scheme, host, port, path, sparql, graphUri, namedGraphUri, authorization, - timeout); + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", + sync.getSubscribes() != subscribers.size()); + assertFalse("Events:" + sync.getEvents() + "(" + events + ")", sync.getEvents() != events); } - protected static SubscribeRequest buildSubscribeRequest(String id, long timeout) - throws SEPAPropertiesException, SEPASecurityException { - String sparql = properties.getSPARQLQuery(id); - String graphUri = properties.getDefaultGraphURI(id); - String namedGraphUri = properties.getNamedGraphURI(id); - + private static UpdateRequest buildUpdateRequest(String id, long timeout) { String authorization = null; - if (sm != null) - authorization = sm.getAuthorizationHeader(); - return new SubscribeRequest(sparql, null, graphUri, namedGraphUri, authorization, timeout); - } - - private UnsubscribeRequest buildUnsubscribeRequest(String spuid, long timeout) - throws SEPAPropertiesException, SEPASecurityException { - String authorization = null; if (sm != null) - authorization = sm.getAuthorizationHeader(); - - return new UnsubscribeRequest(spuid, authorization, timeout); - } - - @Override - public void onSemanticEvent(Notification notify) { - logger.trace("@onSemanticEvent " + notify); - synchronized (events) { - events.set(events.get() + 1); - logger.debug("Notifications received: " + events.get()); - events.notify(); - } - } - - @Override - public void onBrokenConnection() { - logger.trace("@onBrokenConnection"); - } + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } - @Override - public void onError(ErrorResponse errorResponse) { - logger.trace("@onError"); - logger.error(errorResponse.toString()); + return new UpdateRequest(properties.getUpdateMethod(id), properties.getUpdateProtocolScheme(id), + properties.getUpdateHost(id), properties.getUpdatePort(id), properties.getUpdatePath(id), + properties.getSPARQLUpdate(id), properties.getUsingGraphURI(id), properties.getUsingNamedGraphURI(id), + authorization, timeout); } - @Override - public void onSubscribe(String spuid, String alias) { - logger.trace("@onSubscribe: " + spuid + " alias: " + alias); - - synchronized (subscribes) { - subscribes.set(subscribes.get() + 1); - subscribes.notify(); - } - - synchronized (spuidMutex) { - this.spuid = spuid; - spuidMutex.notify(); - } - - } + private static QueryRequest buildQueryRequest(String id, long timeout) { + String authorization = null; - @Override - public void onUnsubscribe(String spuid) { - logger.trace("@onUnsubscribe: " + spuid); + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } - synchronized (subscribes) { - subscribes.set(subscribes.get() - 1); - subscribes.notify(); - } + return new QueryRequest(properties.getQueryMethod(id), properties.getQueryProtocolScheme(id), + properties.getQueryHost(id), properties.getQueryPort(id), properties.getQueryPath(id), + properties.getSPARQLQuery(id), properties.getDefaultGraphURI(id), properties.getNamedGraphURI(id), + authorization, timeout); } } \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java index 24041cb1..f2cd418b 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -2,19 +2,25 @@ import java.util.concurrent.atomic.AtomicLong; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties.HTTPMethod; import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.pattern.JSAP; public class Publisher extends Thread { - private JSAP properties; - private SEPASecurityManager sm; - private SPARQL11Protocol client = null; - private String id; + protected final Logger logger = LogManager.getLogger(); + + private final JSAP properties; + private final SEPASecurityManager sm; + private final SPARQL11Protocol client; + private final String id; private AtomicLong running; @@ -33,34 +39,32 @@ public Publisher(String id,JSAP properties, SEPASecurityManager sm,long n) { public void run() { while(running.get() > 0) { - try { - client.update(buildUpdateRequest(id,5000)); - } catch (SEPAPropertiesException | SEPASecurityException e) { - + Response ret = client.update(buildUpdateRequest(id,5000)); + if (ret.isError()) { + ErrorResponse error = (ErrorResponse) ret; + logger.error(error); + if (error.isTokenExpiredError()) { + client.update(buildUpdateRequest(id,5000)); + } } running.set(running.get()-1); } } - public void interrupt() { - super.interrupt(); + public void finish() { running.set(0); } - protected UpdateRequest buildUpdateRequest(String id, int timeout) throws SEPAPropertiesException, SEPASecurityException { - HTTPMethod method = properties.getUpdateMethod(id); - String scheme = properties.getUpdateProtocolScheme(id); - String host = properties.getUpdateHost(id); - int port = properties.getUpdatePort(id); - String path = properties.getUpdatePath(id); - String sparql = properties.getSPARQLUpdate(id); - String graphUri = properties.getUsingGraphURI(id); - String namedGraphUri = properties.getUsingNamedGraphURI(id); - - String authorization = null; - if (sm != null) authorization = sm.getAuthorizationHeader(); + protected UpdateRequest buildUpdateRequest(String id, int timeout) { + String authorization = null; + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } - return new UpdateRequest(method, scheme, host, port, path, sparql, graphUri, namedGraphUri, + return new UpdateRequest(properties.getUpdateMethod(id), properties.getUpdateProtocolScheme(id), properties.getUpdateHost(id), properties.getUpdatePort(id), properties.getUpdatePath(id), properties.getSPARQLUpdate(id), properties.getUsingGraphURI(id), properties.getUsingNamedGraphURI(id), authorization,timeout); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java index ac4c69b0..8e1199b9 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -2,78 +2,135 @@ import java.util.concurrent.atomic.AtomicBoolean; -import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.pattern.JSAP; -public class Subscriber extends Thread { - - private JSAP properties; - private SEPASecurityManager sm; - private SPARQL11SEProtocol client = null; - private SubscriptionProtocol protocol = null; - private String id; +public class Subscriber implements ISubscriptionHandler { + protected final Logger logger = LogManager.getLogger(); - private AtomicBoolean running = new AtomicBoolean(true); - - public Subscriber(String id, JSAP properties, SEPASecurityManager sm, ISubscriptionHandler handler) - throws SEPAProtocolException { + private final JSAP properties; + private final SEPASecurityManager sm; + private final SPARQL11SEProtocol client; + private final String id; + private final Sync sync; + private String spuid; + + private AtomicBoolean subscribing = new AtomicBoolean(false); + private AtomicBoolean unsubscribing = new AtomicBoolean(false); + + + public Subscriber(String id, JSAP properties, SEPASecurityManager sm, Sync sync) throws SEPAProtocolException, SEPASecurityException { this.properties = properties; this.sm = sm; this.id = id; + this.sync = sync; + SubscriptionProtocol protocol; if (sm != null) { protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), - properties.getSubscribePath(), sm, handler); - client = new SPARQL11SEProtocol(protocol, sm); + properties.getSubscribePath(), sm, this); } else { protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), - properties.getSubscribePath(), handler); - client = new SPARQL11SEProtocol(protocol); + properties.getSubscribePath(), this); } + + client = new SPARQL11SEProtocol(protocol); } - public void run() { - try { - client.subscribe(buildSubscribeRequest(id, 5000)); - } catch (SEPAPropertiesException | SEPASecurityException | SEPAProtocolException e1) { - return; - } + public void close() { + client.close(); + } + + public void subscribe() throws SEPAProtocolException, InterruptedException { + subscribing.set(true); + client.subscribe(buildSubscribeRequest(id, 5000)); + } + + public void unsubscribe(String spuid) throws SEPAProtocolException, InterruptedException { + unsubscribing.set(true); + client.unsubscribe(buildUnsubscribeRequest(spuid, 5000)); + } + + @Override + public void onSemanticEvent(Notification notify) { + logger.debug("@onSemanticEvent: " + notify); + + sync.event(); + } - while (running.get()) { - synchronized (running) { + @Override + public void onBrokenConnection() { + logger.debug("@onBrokenConnection"); + } + + @Override + public void onError(ErrorResponse errorResponse) { + logger.error("@onError: " + errorResponse); + if (errorResponse.isTokenExpiredError()) { + if (subscribing.get()) try { - running.wait(); - } catch (InterruptedException e) { - running.set(false); + client.subscribe(buildSubscribeRequest(id, 5000)); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); } - } + else if (unsubscribing.get()) + try { + client.unsubscribe(buildUnsubscribeRequest(spuid, 5000)); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); + } } - - client.close(); } - public void finish() { - synchronized (running) { - running.set(false); - running.notify(); - } + @Override + public void onSubscribe(String spuid, String alias) { + logger.debug("@onSubscribe: " + spuid + " alias: " + alias); + + subscribing.set(false); + sync.subscribe(spuid, alias); } - private SubscribeRequest buildSubscribeRequest(String id, long timeout) - throws SEPAPropertiesException, SEPASecurityException { - String sparql = properties.getSPARQLQuery(id); - String graphUri = properties.getDefaultGraphURI(id); - String namedGraphUri = properties.getNamedGraphURI(id); + @Override + public void onUnsubscribe(String spuid) { + logger.debug("@onUnsubscribe: " + spuid); + + unsubscribing.set(false); + sync.unsubscribe(); + } - String authorization = null; + private SubscribeRequest buildSubscribeRequest(String id, long timeout) { + String authorization = null; if (sm != null) - authorization = sm.getAuthorizationHeader(); + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + + return new SubscribeRequest(properties.getSPARQLQuery(id), id, properties.getDefaultGraphURI(id), + properties.getNamedGraphURI(id), authorization, timeout); + } - return new SubscribeRequest(sparql, id, graphUri, namedGraphUri, authorization, timeout); + private UnsubscribeRequest buildUnsubscribeRequest(String spuid, long timeout) { + String authorization = null; + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + + return new UnsubscribeRequest(spuid, authorization, timeout); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java new file mode 100644 index 00000000..0a640538 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java @@ -0,0 +1,97 @@ +package it.unibo.arces.wot.sepa.api; + +import java.util.concurrent.atomic.AtomicLong; + +public class Sync { + private static final AtomicLong events = new AtomicLong(0); + private static final AtomicLong subscribes = new AtomicLong(0); + private static final AtomicLong unsubscribes = new AtomicLong(0); + private String spuid = null; + + public void reset() { + subscribes.set(0); + events.set(0); + unsubscribes.set(0); + spuid = null; + } + + public long getSubscribes() { + return subscribes.get(); + } + + public long getEvents() { + return events.get(); + } + + public long getUnsubscribes() { + return unsubscribes.get(); + } + + public void resetSpuid() { + spuid = null; + } + + public String getSpuid() { + return spuid; + } + + public void waitSubscribes(int total) { + while (subscribes.get() < total) { + synchronized (subscribes) { + try { + subscribes.wait(1000); + } catch (InterruptedException e) { + + } + } + } + } + + public void waitEvents(int total) { + while (events.get() < total) { + synchronized (events) { + try { + events.wait(1000); + } catch (InterruptedException e) { + + } + } + } + } + + public void waitUnsubscribes(int total) { + while (unsubscribes.get() < total) { + synchronized (unsubscribes) { + try { + unsubscribes.wait(1000); + } catch (InterruptedException e) { + + } + } + } + + } + + public synchronized void event() { + synchronized (events) { + events.set(events.get() + 1); + events.notify(); + } + } + + public synchronized void unsubscribe() { + synchronized (unsubscribes) { + unsubscribes.set(unsubscribes.get() + 1); + unsubscribes.notify(); + } + } + + public synchronized void subscribe(String spuid,String alias) { + synchronized (subscribes) { + this.spuid = spuid; + subscribes.set(subscribes.get() + 1); + subscribes.notify(); + } + } + +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java new file mode 100644 index 00000000..a6a92629 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java @@ -0,0 +1,56 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import static org.junit.Assert.assertFalse; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.junit.Test; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.protocols.websocket.SEPAWebsocketClient; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +public class ITSEPAWebsocketClient implements ISubscriptionHandler { + protected final Logger logger = LogManager.getLogger(); + + @Test(timeout=10000) + public void Connect() throws InterruptedException, URISyntaxException, IOException, SEPASecurityException { + for (int i=0; i < 100; i++) { + SEPASecurityManager sm = new SEPASecurityManager(); + SEPAWebsocketClient client = new SEPAWebsocketClient(new URI("wss://localhost:9443/secure/subscribe"),this,sm.getSSLSocket()); + assertFalse("Failed to connect",!client.connectBlocking()); + } + } + + @Override + public void onSemanticEvent(Notification notify) { + logger.debug("@onSemanticEvent: "+notify); + } + + @Override + public void onBrokenConnection() { + logger.debug("@onBrokenConnection"); + } + + @Override + public void onError(ErrorResponse errorResponse) { + logger.debug("@onError: "+errorResponse); + } + + @Override + public void onSubscribe(String spuid, String alias) { + logger.debug("@onSubscribe: "+spuid+ " alias: "+alias); + } + + @Override + public void onUnsubscribe(String spuid) { + logger.debug("@onUnsubscribe: "+spuid); + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java new file mode 100644 index 00000000..2ae2fc86 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java @@ -0,0 +1,61 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +import org.junit.Test; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +import static org.junit.Assert.assertFalse; + +public class ITWebSocketClient { + protected final Logger logger = LogManager.getLogger(); + + class WebsocketTest extends WebSocketClient { + + public WebsocketTest(URI serverUri) { + super(serverUri); + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + logger.debug("@onOpen: "+handshakedata.getHttpStatusMessage()); + } + + @Override + public void onMessage(String message) { + logger.debug("@onMessage: "+message); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + logger.debug("@onClose code:"+code+" reason:"+reason+" remote:"+remote); + } + + @Override + public void onError(Exception ex) { + logger.error(ex.getMessage()); + } + + } + + @Test(timeout=20000) + public void Connect() throws URISyntaxException, InterruptedException, SEPASecurityException, IOException { + SEPASecurityManager sm = new SEPASecurityManager(); + + for (int i=0; i < 100; i++) { + WebsocketTest client = new WebsocketTest(new URI("wss://localhost:9443/secure/subscribe")); + client.setSocket(sm.getSSLSocket()); + assertFalse("Failed to connect",!client.connectBlocking()); + } + } + +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 955761f8..10514531 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -1,18 +1,17 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - +import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -43,15 +42,17 @@ public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException { app = ConfigurationProvider.GetTestEnvConfiguration(); - if (app.isSecure()) + + if (app.isSecure()) { sm = new SEPASecurityManager(app.getAuthenticationProperties()); + sm.register("SEPATest"); + } } @Before public void before() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, URISyntaxException { if (app.isSecure()) { - sm.register("SEPATest"); client = new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, this); } else @@ -62,6 +63,11 @@ public void before() subscribes.set(0); brokens.set(0); } + + @After + public void after() { + + } @Test(timeout = 5000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { @@ -92,94 +98,94 @@ public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, S assertFalse("Failed to subscribe",subscribes.get() != 1); } - @Test(timeout = 20000) - public void BrokenSockets() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - int n = 5; - - SubscribeRequest request; - ArrayList threadPoll = new ArrayList(); - Thread th; - - for (int i = 0; i < n; i++) { - if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); - } else - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), null, 5000); - th = new Thread(new WebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, - request,this)); - threadPoll.add(th); - th.start(); - - if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); - } else - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), null, 5000); - th = new Thread(new WebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, - request,this)); - threadPoll.add(th); - th.start(); - - if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", - app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), sm.getAuthorizationHeader(), - 5000); - } else - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", - app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), null, 5000); - th = new Thread(new WebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, - request,this)); - threadPoll.add(th); - th.start(); - } - - while(subscribes.get() != n * 3) { - synchronized(subscribes) { - try { - subscribes.wait(); - } catch (InterruptedException e) { - - } - } - } - - assertFalse("Failed to subscribe",subscribes.get() != n * 3); - - for (Thread th1 : threadPoll) { - try { - th1.join(); - } catch (InterruptedException e) { - - } - } - - while(events.get() != n * 3) { - synchronized(events) { - try { - events.wait(); - } catch (InterruptedException e) { - - } - } - } - - assertFalse("Failed to receive all first notifications",events.get() != n * 3); - - while(brokens.get() != n * 3) { - synchronized(brokens) { - try { - brokens.wait(); - } catch (InterruptedException e) { - - } - } - } - - assertFalse("Failed to receive all broken notifications",brokens.get() != n * 3); - } +// @Test(timeout = 20000) +// public void BrokenSockets() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { +// int n = 5; +// +// SubscribeRequest request; +// ArrayList threadPoll = new ArrayList(); +// Thread th; +// +//// for (int i = 0; i < n; i++) { +//// if (app.isSecure()) { +//// request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), +//// app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); +//// } else +//// request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), +//// app.getNamedGraphURI("ALL"), null, 5000); +//// th = new Thread(new ITSEPAWebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, +//// request,this)); +//// threadPoll.add(th); +//// th.start(); +//// +//// if (app.isSecure()) { +//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), +//// app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); +//// } else +//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), +//// app.getNamedGraphURI("RANDOM"), null, 5000); +//// th = new Thread(new ITSEPAWebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, +//// request,this)); +//// threadPoll.add(th); +//// th.start(); +//// +//// if (app.isSecure()) { +//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", +//// app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), sm.getAuthorizationHeader(), +//// 5000); +//// } else +//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", +//// app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), null, 5000); +//// th = new Thread(new ITSEPAWebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, +//// request,this)); +//// threadPoll.add(th); +//// th.start(); +//// } +// +// while(subscribes.get() != n * 3) { +// synchronized(subscribes) { +// try { +// subscribes.wait(); +// } catch (InterruptedException e) { +// +// } +// } +// } +// +// assertFalse("Failed to subscribe",subscribes.get() != n * 3); +// +// for (Thread th1 : threadPoll) { +// try { +// th1.join(); +// } catch (InterruptedException e) { +// +// } +// } +// +// while(events.get() != n * 3) { +// synchronized(events) { +// try { +// events.wait(); +// } catch (InterruptedException e) { +// +// } +// } +// } +// +// assertFalse("Failed to receive all first notifications",events.get() != n * 3); +// +// while(brokens.get() != n * 3) { +// synchronized(brokens) { +// try { +// brokens.wait(); +// } catch (InterruptedException e) { +// +// } +// } +// } +// +// assertFalse("Failed to receive all broken notifications",brokens.get() != n * 3); +// } @Test(timeout = 5000) public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java deleted file mode 100644 index 805c597e..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/WebsocketClient.java +++ /dev/null @@ -1,39 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import static org.junit.Assert.assertFalse; - -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.protocols.WebsocketSubscriptionProtocol; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -public class WebsocketClient implements Runnable { - private WebsocketSubscriptionProtocol client = null; - private SubscribeRequest request = null; - - public WebsocketClient(String host,int port,String path,SEPASecurityManager sm,SubscribeRequest request,ISubscriptionHandler handler) throws SEPAProtocolException { - if (sm != null) client = new WebsocketSubscriptionProtocol(host, port, - path, sm, handler); - else client = new WebsocketSubscriptionProtocol(host, port, - path, handler); - this.request = request; - } - - - public void run() { - try { - client.subscribe(request); - } catch (SEPAProtocolException e1) { - assertFalse(e1.getMessage(),true); - } - - try { - Thread.sleep((long) (500+500*Math.random())); - } catch (InterruptedException e) { - - } - - client.close(); - } -} diff --git a/client-api/src/test/resources/log4j2.xml b/client-api/src/test/resources/log4j2.xml index be72e020..639d3c4c 100644 --- a/client-api/src/test/resources/log4j2.xml +++ b/client-api/src/test/resources/log4j2.xml @@ -15,7 +15,7 @@ DEBUG 500 TRACE 600 ALL Integer.MAX_VALUE --> - + @@ -26,8 +26,8 @@ ALL Integer.MAX_VALUE - - + + diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index f140bee2..780f8406 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -47,13 +47,13 @@ "sparql" : "delete where {?x ?y ?z}" }, "VAIMEE": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P \"ვაიმეე\"} where {OPTIONAL{?s ?p ?o}}" + "sparql": "delete {sepa:SV sepa:PV ?o} where {sepa:SV sepa:PV ?o} ; insert data {sepa:SV sepa:PV \"ვაიმეე\"}" }, "RANDOM": { - "sparql": "delete {?s ?p ?o} insert {sepa:S sepa:P ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "delete {sepa:S sepa:P ?o} where {sepa:S sepa:P ?o} ; insert {sepa:S sepa:P ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" }, "RANDOM1": { - "sparql": "delete {?s ?p ?o} insert {sepa:S1 sepa:P1 ?random} where {OPTIONAL{?s ?p ?o} BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "delete {sepa:S1 sepa:P1 ?o} where {sepa:S1 sepa:P1 ?o} ; insert {sepa:S1 sepa:P1 ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" } }, "queries": { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java index 8ef4d799..01152c8b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SchedulerBeans.java @@ -17,6 +17,8 @@ public class SchedulerBeans { private static int queueSize; + private static int timeout; + public static long getErrors() { return errors; } @@ -82,4 +84,12 @@ public static void setQueueSize(int schedulingQueueSize) { public static int getQueueSize() { return queueSize; } + + public static int getTimeout() { + return timeout; + } + + public static void setTimeout(int t) { + timeout = t; + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java index da2930d5..f0ebb11b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java @@ -11,12 +11,16 @@ public class SubscribeProcessorBeans { private static long activeSPUs = 0; private static long maxActiveSPUs = 0; - private static long subscribeRequests; - private static long unsubscribeRequests; + private static long subscribeRequests = 0; + private static long unsubscribeRequests = 0; - private static int SPUProcessingTimeout; + private static int SPUProcessingTimeout = 30000; private static long unitScale = 1000000; + + private static int subscribers = 0; + + private static long subscribers_max = 0; public static void scale_ms() { unitScale = 1000000; @@ -90,6 +94,9 @@ public static void reset() { subscribeRequests = 0; unsubscribeRequests = 0; + subscribers = 0; + subscribers_max = 0; + } public static float getSPUs_time() { @@ -123,4 +130,21 @@ public static int getSPUProcessingTimeout() { public static void setSPUProcessingTimeout(int t) { SPUProcessingTimeout = t; } + + public static void registerHandler() { + subscribers++; + if (subscribers > subscribers_max) subscribers_max = subscribers; + } + + public static void unregisterHandler() { + subscribers--; + } + + public static int getSubscribers() { + return subscribers; + } + + public static long getSubscribersMax() { + return subscribers_max; + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java index 353f2280..c8a9c201 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/WebsocketBeans.java @@ -20,6 +20,11 @@ public class WebsocketBeans { private static long unsubscribeHandlingMaxTime = -1; private static long handledunsubscribes = 0; + private static long subscribeResponses = 0; + private static long unsubscribeResponses = 0; + private static long errorResponses = 0; + + private static long notifications = 0; public static long unsubscribeTimings(long start) { handledunsubscribes++; @@ -86,6 +91,11 @@ public static void reset() { unsubscribeHandlingMinTime = -1; unsubscribeHandlingMaxTime = -1; handledunsubscribes = 0; + + subscribeResponses=0; + unsubscribeResponses = 0; + errorResponses = 0; + notifications = 0; } public static long getMessages(){ @@ -119,4 +129,37 @@ public static void onNotAuthorizedRequest() { public static void onMessage() { messages++; } + + public static void subscribeResponse() { + subscribeResponses++; + } + + public static long getSubscribeResponses() { + return subscribeResponses; + } + + public static void unsubscribeResponse() { + unsubscribeResponses++; + } + + public static long getUnsubscribeResponses() { + return unsubscribeResponses; + } + + public static void errorResponse() { + errorResponses++; + } + + public static long getErrorResponses() { + return errorResponses; + } + + public static void notification() { + notifications++; + } + + public static long getNotifications() { + return notifications; + } + } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 18efa622..9cece7d1 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -216,7 +216,10 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException // Initialize SPARQL 1.1 SE processing service properties try { properties = new EngineProperties(engineJpar); - } catch (SEPAPropertiesException e) {} + } catch (SEPAPropertiesException e) { + System.err.println(e.getLocalizedMessage()); + System.exit(1); + } EngineBeans.setEngineProperties(properties); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java index f8c5c7ec..8740c129 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java @@ -169,28 +169,121 @@ protected void defaults() { protected void validate() throws SEPAPropertiesException { try { properties.get("scheduler").getAsJsonObject().get("queueSize").getAsInt(); - + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("scheduler-queueSize is missing")); + } + + try { + properties.get("scheduler").getAsJsonObject().get("timeout").getAsInt(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("scheduler-timeout is missing")); + } + + try { properties.get("processor").getAsJsonObject().get("updateTimeout").getAsInt(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("processor-updateTimeout is missing")); + } + + try { properties.get("processor").getAsJsonObject().get("queryTimeout").getAsInt(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("processor-queryTimeout is missing")); + } + + try { properties.get("processor").getAsJsonObject().get("maxConcurrentRequests").getAsInt(); - + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("processor-maxConcurrentRequests is missing")); + } + + try { properties.get("spu").getAsJsonObject().get("timeout").getAsInt(); - + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("spu-timeout is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("secure").getAsBoolean(); - + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-secure is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("http").getAsInt(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-ports-http is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("https").getAsInt(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-ports-https is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("ws").getAsInt(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-ports-ws is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("wss").getAsInt(); - + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-ports-wss is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("securePath").getAsString(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-paths-securePath is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("update").getAsString(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-paths-update is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("query").getAsString(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-paths-query is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("subscribe").getAsString(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-paths-subscribe is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("register").getAsString(); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-paths-register is missing")); + } + + try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("tokenRequest").getAsString(); - } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("Failed to validate jpar: " + e.getMessage())); + } + catch (Exception e) { + throw new SEPAPropertiesException(new Exception("gates-paths-tokenRequest is missing")); } } @@ -340,4 +433,12 @@ public boolean isUpdateReliable() { } } + public int getSchedulerTimeout() { + try { + return properties.get("scheduler").getAsJsonObject().get("timeout").getAsInt(); + } catch (Exception e) { + return 60000; + } + } + } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java index 8baea394..8e5397d2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java @@ -38,8 +38,9 @@ import javax.net.ssl.SSLContext; -import org.apache.http.Header; -import org.apache.http.HttpRequest; +//import org.apache.http.Header; +//import org.apache.http.HttpRequest; + import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -139,7 +140,7 @@ private void securityCheck(String identity) { if(!valid.getClass().equals(ErrorResponse.class)) logger.debug("PASSED"); else { ErrorResponse error = (ErrorResponse) valid; - logger.debug("FAILED Code: "+error.getErrorCode()+ "Message: "+error.getErrorMessage()); + logger.error(error); } } else logger.debug("FAILED"); @@ -152,26 +153,26 @@ private void securityCheck(String identity) { removeAuthorizedIdentity(identity); } - /** - * Gets the RSA Key from the keystore. - * - * @param keyAlias - * the key alias - * @param keyPwd - * the key password - * @return the RSAKey - * @throws JOSEException - * @throws KeyStoreException - * - * @see RSAKey - */ - public RSAKey getJWK(String keyAlias, String keyPwd) throws KeyStoreException, JOSEException { - RSAKey jwk = null; - - jwk = RSAKey.load(sManager.getKeyStore(), keyAlias, keyPwd.toCharArray()); - - return jwk; - } +// /** +// * Gets the RSA Key from the keystore. +// * +// * @param keyAlias +// * the key alias +// * @param keyPwd +// * the key password +// * @return the RSAKey +// * @throws JOSEException +// * @throws KeyStoreException +// * +// * @see RSAKey +// */ +// private RSAKey getJWK(String keyAlias, String keyPwd) throws KeyStoreException, JOSEException { +// RSAKey jwk = null; +// +// jwk = RSAKey.load(sManager.getKeyStore(), keyAlias, keyPwd.toCharArray()); +// +// return jwk; +// } private boolean init(KeyStore keyStore,String keyAlias,String keyPwd) throws KeyStoreException, JOSEException{ // Load the key from the key store @@ -217,44 +218,50 @@ public AuthorizationManager(String keystoreFileName,String keystorePwd,String ke /** * Operation when receiving a HTTP request at a protected endpoint * - * 1. Check if the request contains an Authorization header. 2. Check if the - * request contains an Authorization: Bearer-header with non-null/empty - * contents 3. Check if the value of the Authorization: Bearer-header is a - * JWT object 4. Check if the JWT object is signed 5. Check if the signature - * of the JWT object is valid. This is to be checked with AS public - * signature verification key 6. Check the contents of the JWT object 7. - * Check if the value of "iss" is - * https://wot.arces.unibo.it:8443/oauth/token 8. Check if the value of - * "aud" contains https://wot.arces.unibo.it:8443/sparql 9. Accept the - * request as well as "sub" as the originator of the request and process it - * as usual - * - * *** Respond with 401 if not - */ - public boolean authorizeRequest(HttpRequest request) { - // Extract Bearer authorization - Header[] bearer = request.getHeaders("Authorization"); +
    +1. Check if the request contains an Authorization header. 
     
    -		if (bearer.length != 1) {
    -			logger.error("Authorization header is missing or multiple");
    -			return false;
    -		}
    -		if (!bearer[0].getValue().startsWith("Bearer ")) {
    -			logger.error("Authorization must be \"Bearer JWT\"");
    -			return false;
    -		}
    +2. Check if the request contains an Authorization: Bearer-header with non-null/empty contents 
     
    -		// ******************
    -		// JWT validation
    -		// ******************
    -		String jwt = bearer[0].getValue().split(" ")[1];
    +3. Check if the value of the Authorization: Bearer-header is a JWT object 
     
    -		Response valid = validateToken(jwt);
    +4. Check if the JWT object is signed 
     
    -		if (valid.getClass().equals(ErrorResponse.class)) return false;
    -		
    -		return true;
    -	}
    +5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    +
    +6. Check the contents of the JWT object 
    +
    +7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    +
    +8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    +
    +9. Accept the request as well as "sub" as the originator of the request and process it as usual
    + 
    +Respond with 401 if not
    + 
    +
    + */ +// private synchronized Response authorizeRequest(HttpRequest request) { +// // Extract Bearer authorization +// Header[] bearer = request.getHeaders("Authorization"); +// +// if (bearer.length != 1) { +// logger.error("Authorization header is missing or multiple"); +// return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_request","Authorization header must be a single one"); +// } +// if (!bearer[0].getValue().startsWith("Bearer ")) { +// logger.error("Authorization must be \"Bearer JWT\""); +// return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_request","Authorization header must be \"Bearer JWT\""); +// } +// +// // ****************** +// // JWT validation +// // ****************** +// String jwt = bearer[0].getValue().split(" ")[1]; +// +// return validateToken(jwt); +// } + private boolean authorizeIdentity(String id) { logger.debug("Authorize identity:"+id); @@ -275,40 +282,43 @@ private boolean authorizeIdentity(String id) { } /** + *
     	 * POST https://wot.arces.unibo.it:8443/oauth/token
    -	 * {@code
    +	 * 
     	 * Accept: application/json
     	 * Content-Type: application/json
     	 * 
     	 * { 
    -	 * "client_identity": ”", 
    -	 * "grant_types": ["client_credentials"] 
    -	 * }
    +	 *  "client_identity": ”", 
    +	 *  "grant_types": ["client_credentials"] 
     	 * }
    +	 * 
     	 * Response example:
    -	 * {@code
    -	 * { 	"clientId": "889d02cf-16dd-4934-9341-a754088faxyz",
    -	 * 		"clientSecret": "ahd5MU42J0hIxPXzhUhjJHt2d0Oc5M6B644CtuwUlE9zpSuF14-kXYZ",
    -	 * 		"signature" : JWK RSA public key (can be used to verify the signature),
    -	 * 		"authorized" : Boolean
    -	 * }
    +	 *
    +	 * {
    +	 *  "clientId": "889d02cf-16dd-4934-9341-a754088faxyz",
    +	 *  "clientSecret": "ahd5MU42J0hIxPXzhUhjJHt2d0Oc5M6B644CtuwUlE9zpSuF14-kXYZ",
    +	 *  "signature" : JWK RSA public key (can be used to verify the signature),
    +	 *  "authorized" : Boolean
     	 * }
    +	 * 
     	 * In case of error, the following applies:
    -	 * {@code
    -	 * {
    -	 * 		"code": Error code,
    -	 * 		"body": "Error details" (optional)
    + {
    +   "error":"Unless specified otherwise see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol",
    +   "error_description":"Unless specified otherwise, see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol", (OPTIONAL)
    +   "status_code" : the HTTP status code (would be 400 for Oauth 2.0 errors).
    + }
    +	 * 
    * - * } - * } + * @param identity the client identity to be registered * */ - public Response register(String identity) { - logger.debug("Register: "+identity); + public synchronized Response register(String identity) { + logger.info("REGISTER: "+identity); //Check if entity is authorized to request credentials if (!authorizeIdentity(identity)) { logger.error("Not authorized identity "+identity); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Not authorized identity "+identity); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"not_authorized_identity","Client "+identity+" is not authorized"); } String client_id = null; @@ -316,7 +326,7 @@ public Response register(String identity) { //Check if identity has been already registered if (clients.containsKey(identity)) { - logger.warn("Giving credentials to a registred identity "+identity); + logger.warn("Giving credentials to a registered identity "+identity); client_id = clients.get(identity); client_secret = credentials.get(client_id); } @@ -335,31 +345,41 @@ public Response register(String identity) { return new RegistrationResponse(client_id,client_secret,jwkPublicKey); } - /** - * POST https://wot.arces.unibo.it:8443/oauth/token - * - * Content-Type: application/x-www-form-urlencoded - * Accept: application/json - * Authorization: Basic Basic64(id:secret) - * - * Response example: - * { "access_token": "eyJraWQiOiIyN.........", - * "token_type": "bearer", - * "expires_in": 3600 - * } - * - * In case of error, the following applies: - * { - * "code": Error code, - * "body": "Error details" - * } - * - * The error codes correspond to an HTTP status codes as follows: - * --- SC_UNAUTHORIZED (401) : client not authorized - * --- SC_BAD_REQUEST (400) : the token is not expired - * --- SC_INTERNAL_SERVER_ERROR (500) : error validating the token - * */ - public Response getToken(String encodedCredentials) { +/** + It requests a token to the Authorization Server. A token request should be made when the current token is expired or it is the first token. + If the token is not expired, the "invalid_grant" error is returned. + + @param encodedCredentials the client credentials encoded using Base64 + @return JWTResponse in case of success, ErrorResponse otherwise + @see JWTResponse + @see ErrorResponse + +
    +POST https://wot.arces.unibo.it:8443/oauth/token
    + 
    +Content-Type: application/x-www-form-urlencoded
    +Accept: application/json
    +Authorization: Basic Basic64(id:secret)
    + 
    +Response example:
    +{
    +"access_token": "eyJraWQiOiIyN.........",
    +"token_type": "bearer",
    +"expires_in": 3600 
    +}
    +
    + Error response example:
    + {
    +   "error":"Unless specified otherwise see RFC6749. Otherwise, this is specific of the SPARQL 1.1 SE Protocol",
    +   "error_description":"Unless specified otherwise, see RFC6749. Otherwise, this is specific to the SPARQL 1.1 SE Protocol", (OPTIONAL)
    +   "status_code" : the HTTP status code (should be 400 for all Oauth 2.0 errors).
    + }
    +
    +According to RFC6749, the error member can assume the following values: invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, invalid_scope.
    + 
    +*/ + + public synchronized Response getToken(String encodedCredentials) { logger.debug("Get token"); //Decode credentials @@ -369,17 +389,19 @@ public Response getToken(String encodedCredentials) { } catch (IllegalArgumentException e) { logger.error("Not authorized"); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"invalid_client",e.getMessage()); } + + // Parse credentials String decodedCredentials = new String(decoded); String[] clientID = decodedCredentials.split(":"); if (clientID==null){ logger.error("Wrong Basic authorization"); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"invalid_client","Client id not found: "+decodedCredentials); } if (clientID.length != 2) { logger.error("Wrong Basic authorization"); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"invalid_client","Wrong credentials: "+decodedCredentials); } String id = decodedCredentials.split(":")[0]; @@ -389,30 +411,36 @@ public Response getToken(String encodedCredentials) { //Verify credentials if (!credentials.containsKey(id)) { logger.error("Client id: "+id+" is not registered"); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","Client "+id+" not found"); } if (!credentials.get(id).equals(secret)) { logger.error("Wrong secret: "+secret+ " for client id: "+id); - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Client not authorized"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","Client not authorized"); } - //Check is a token has been release for this client + // Prepare JWT with claims set + JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); + Date now = new Date(); + + // If not yet expired return an error if (clientClaims.containsKey(id)) { - //Do not return a new token if the previous one is not expired - Date expires = clientClaims.get(id).getExpirationTime(); - Date now = new Date(); - logger.debug("Check token expiration: "+now+" > "+expires+ " ?"); - if(now.before(expires)) { - logger.warn("Token is not expired"); - return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"Token is not expired"); + Date expiring = clientClaims.get(id).getExpirationTime(); + + long delta = expiring.getTime()-now.getTime(); + // Expires if major than current time + logger.debug("ID: "+id+" ==> Token will expire in: "+delta+" ms"); + if(delta > 0) { + logger.warn("Token is NOT EXPIRED"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant",now+" token will expire on "+expiring); } + logger.debug("Token is EXPIRED. Release a fresh token."); } - // Prepare JWT with claims set - JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder(); - long timestamp = new Date().getTime(); - + // Define validity period + //Date before = new Date(now.getTime()-1000); + Date expires = new Date(now.getTime()+(AuthorizationManagerBeans.getTokenExpiringPeriod()*1000)); + /* * 4.1.1. "iss" (Issuer) Claim @@ -464,7 +492,7 @@ public Response getToken(String encodedCredentials) { a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.*/ - claimsSetBuilder.expirationTime(new Date(timestamp+(AuthorizationManagerBeans.getTokenExpiringPeriod()*1000))); + claimsSetBuilder.expirationTime(expires); /*4.1.5. "nbf" (Not Before) Claim @@ -476,7 +504,7 @@ public Response getToken(String encodedCredentials) { account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.*/ - claimsSetBuilder.notBeforeTime(new Date(timestamp-1000)); + // claimsSetBuilder.notBeforeTime(before); /* 4.1.6. "iat" (Issued At) Claim @@ -485,7 +513,7 @@ public Response getToken(String encodedCredentials) { value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.*/ - claimsSetBuilder.issueTime(new Date(timestamp)); + claimsSetBuilder.issueTime(now); /*4.1.7. "jti" (JWT ID) Claim @@ -510,22 +538,48 @@ public Response getToken(String encodedCredentials) { signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), JWTClaimsSet.parse(jwtClaims.toString())); } catch (ParseException e) { logger.error(e.getMessage()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"Error on signing JWT (1)"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","ParseException: "+e.getMessage()); } try { signedJWT.sign(signer); } catch (JOSEException e) { logger.error(e.getMessage()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"Error on signing JWT (2)"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","JOSEException: "+e.getMessage()); } //Add the token to the released tokens clientClaims.put(id, jwtClaims); - return new JWTResponse(signedJWT.serialize(),"bearer",AuthorizationManagerBeans.getTokenExpiringPeriod()); + JWTResponse jwt = new JWTResponse(signedJWT.serialize(),"bearer",AuthorizationManagerBeans.getTokenExpiringPeriod()); + logger.debug("Released token: "+jwt); + + return jwt; } - public Response validateToken(String accessToken) { + /** + * Operation when receiving a request at a protected endpoint + * +
    +Specific to HTTP request:
    +1. Check if the request contains an Authorization header. 
    +2. Check if the request contains an Authorization: Bearer-header with non-null/empty contents 
    +3. Check if the value of the Authorization: Bearer-header is a JWT object 
    +
    +Token validation:
    +4. Check if the JWT object is signed 
    +5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    +6. Check the contents of the JWT object 
    +7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    +8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    +9. Accept the request as well as "sub" as the originator of the request and process it as usual
    + 
    +Respond with 401 if not
    + 
    +
    + +@param accessToken the JWT token to be validate according to points 4-9 + */ + public synchronized Response validateToken(String accessToken) { logger.debug("Validate token"); //Parse and verify the token @@ -533,31 +587,43 @@ public Response validateToken(String accessToken) { try { signedJWT = SignedJWT.parse(accessToken); } catch (ParseException e) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,e.getMessage()); + logger.error(e.getMessage()); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","ParseException: "+e.getMessage()); } try { - if(!signedJWT.verify(verifier)) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Signed JWT not verified"); + if(!signedJWT.verify(verifier)) { + logger.error("Signed JWT not verified"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","Signed JWT not verified"); + } } catch (JOSEException e) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,e.getMessage()); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","JOSEException: "+e.getMessage()); } - // Process the token - JWTClaimsSet claimsSet; + // Process the token (validate) + JWTClaimsSet claimsSet = null; try { claimsSet = jwtProcessor.process(accessToken, context); - } catch (ParseException | BadJOSEException | JOSEException e) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,e.getMessage()); + } catch (ParseException e) { + logger.error(e.getMessage()); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","ParseException: "+e.getMessage()); + } catch (BadJOSEException e) { + logger.error(e.getMessage()); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","BadJOSEException: "+e.getMessage()); + } catch (JOSEException e) { + logger.error(e.getMessage()); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","JOSEException: "+e.getMessage()); } - //Check token expiration - Date now = new Date(); - if (now.after(claimsSet.getExpirationTime())) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Token is expired "+claimsSet.getExpirationTime()); - - if (now.before(claimsSet.getNotBeforeTime())) return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"Token can not be used before: "+claimsSet.getNotBeforeTime()); +// //Check token expiration +// Date now = new Date(); +// Date expiring = claimsSet.getExpirationTime(); +// Date notBefore = claimsSet.getNotBeforeTime(); +// if (expiring.getTime()-now.getTime() <= 0) return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","Token is expired "+claimsSet.getExpirationTime()); +// if (now.getTime() < notBefore.getTime()) return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","Token can not be used before: "+claimsSet.getNotBeforeTime()); - return new JWTResponse(accessToken,"bearer",now.getTime()-claimsSet.getExpirationTime().getTime()); + return new JWTResponse(accessToken,"bearer",claimsSet.getExpirationTime().getTime()-new Date().getTime()); } @Override @@ -626,7 +692,7 @@ public void setSubject(String sub) { AuthorizationManagerBeans.setSubject(sub); } - public SSLContext getSSLContext() throws KeyManagementException, NoSuchAlgorithmException { + public SSLContext getSSLContext() throws SEPASecurityException { return sManager.getSSLContext(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java index 35312a20..b7f9759c 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java @@ -12,62 +12,57 @@ public class DependabilityManager { private static final Logger logger = LogManager.getLogger(); - // Active subscriptions + // Active subscriptions private static final HashMap> subscriptions = new HashMap>(); - // Broken subscriptions - private static final ArrayList brokenBeforeSubscribe = new ArrayList(); - // Scheduler queue private final SchedulerQueue schedulerQueue; - + public DependabilityManager(SchedulerQueue schedulerQueue) { this.schedulerQueue = schedulerQueue; } public synchronized void onSubscribe(Integer hash, String spuid) { - logger.debug("@onSubscribe: "+hash+" SPUID: "+spuid); + logger.debug("@onSubscribe: " + hash + " SPUID: " + spuid); - if (brokenBeforeSubscribe.contains(hash)) { - logger.debug("Subscription has already been closed: " + hash + " Schedule to kill SPUID: " + spuid); - schedulerQueue.killSpuid(spuid); - return; - } - - if (!subscriptions.containsKey(hash)) subscriptions.put(hash, new ArrayList()); + if (!subscriptions.containsKey(hash)) + subscriptions.put(hash, new ArrayList()); + subscriptions.get(hash).add(spuid); - subscriptions.get(hash).add(spuid); + logger.debug("Active subscriptions: "+subscriptions.size()); } public synchronized void onUnsubscribe(Integer hash, String spuid) { - logger.debug("@onUnsubscribe: "+hash+" SPUID: "+spuid); - + logger.debug("@onUnsubscribe: " + hash + " SPUID: " + spuid); + subscriptions.get(hash).remove(spuid); - if (subscriptions.get(hash).isEmpty()) subscriptions.remove(hash); + if (subscriptions.get(hash).isEmpty()) + subscriptions.remove(hash); + + logger.debug("Active subscriptions: "+subscriptions.size()); } public synchronized void onBrokenSocket(Integer hash) { - logger.debug("@onBrokenSocket: "+hash); - - if (!subscriptions.containsKey(hash)) { - logger.debug("Broken before subscribe: "+hash); - brokenBeforeSubscribe.add(hash); - return; - } - + logger.debug("@onBrokenSocket: " + hash); + + if (!subscriptions.containsKey(hash)) return; + logger.debug(String.format("Broken socket with active subscriptions: %d", subscriptions.get(hash).size())); // Kill all SPUs for (String spuid : subscriptions.get(hash)) { - logger.debug("Schedule request to kill SPU: "+spuid); + logger.debug("Schedule request to kill SPU: " + spuid); schedulerQueue.killSpuid(spuid); } - + // Remove subscriptions subscriptions.remove(hash); + + logger.debug("Active subscriptions: "+subscriptions.size()); + } - public synchronized void onError(Integer hash, ErrorResponse error) { - logger.error("Subscription:"+hash + " error:"+error); + public void onError(Integer hash, ErrorResponse error) { + logger.error("Subscription:" + hash + " error:" + error); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java index 8a9e5426..dff017dd 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessor.java @@ -57,9 +57,10 @@ public Response process(InternalQueryRequest req) { if (endpointSemaphore != null) try { + //TODO: timeout endpointSemaphore.acquire(); } catch (InterruptedException e) { - return new ErrorResponse(500, e.getMessage()); + return new ErrorResponse(500, "InterruptedException",e.getMessage()); } // Authorized access to the endpoint diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java index 9f827a71..c6812b29 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java @@ -11,6 +11,7 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; +import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPU; @@ -24,10 +25,10 @@ class SubscribeProcessingThread extends Thread implements SubscribeProcessingThr private final Processor processor; // Maps - private final HashMap> activeSpus = new HashMap>(); - private final HashMap spuids = new HashMap(); - private final HashMap handlers = new HashMap(); - private final HashMap sequenceNumbers = new HashMap(); + private static final HashMap> activeSpus = new HashMap>(); + private static final HashMap spuids = new HashMap(); + private static final HashMap handlers = new HashMap(); + private static final HashMap sequenceNumbers = new HashMap(); // Broken SPUs disposer private final Thread killer; @@ -42,7 +43,9 @@ public void run() { while (processor.isRunning()) { try { String spuid = processor.getSchedulerQueue().waitSpuid2Kill(); - unregisterHandler(spuids.get(spuid), spuid); + synchronized (activeSpus) { + unregisterHandler(spuids.get(spuid), spuid); + } } catch (InterruptedException e) { return; } @@ -50,6 +53,8 @@ public void run() { } }; killer.setName("SEPA-SPU-Killer"); + + SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); } @Override @@ -63,7 +68,7 @@ public void run() { try { // Wait request... ScheduledRequest request = processor.getSchedulerQueue().waitSubscribeUnsubscribeRequest(); - logger.debug(request); + logger.debug(">> "+request); // Process request Response response = null; @@ -72,6 +77,8 @@ public void run() { else if (request.isUnsubscribeRequest()) response = unsubscribe(((InternalUnsubscribeRequest) request.getRequest()).getSpuid()); + logger.debug("<< "+response); + // Send back response processor.getSchedulerQueue().addResponse(request.getToken(), response); @@ -82,7 +89,9 @@ else if (request.isUnsubscribeRequest()) } } - private synchronized Response subscribe(InternalSubscribeRequest req) { + private Response subscribe(InternalSubscribeRequest req) { + SubscribeProcessorBeans.subscribeRequest(); + EventHandler eventHandler = req.getEventHandler(); String sparql = req.getSparql(); String alias = req.getAlias(); @@ -94,7 +103,7 @@ private synchronized Response subscribe(InternalSubscribeRequest req) { // Get an SPU from the SPU manager (already available or a new one) SPU spu = processor.getSPUManager().getSPU(wrappedRequest); if (spu == null) - return new ErrorResponse(500, "Failed to create SPU"); + return new ErrorResponse(500, "internal_server_error", "Failed to create SPU"); // Generate a fake SPU id String spuid = processor.getSPUManager().generateSpuid(); @@ -105,33 +114,39 @@ private synchronized Response subscribe(InternalSubscribeRequest req) { return new SubscribeResponse(spuid, req.getAlias(), spu.getLastBindings()); } - private synchronized Response unsubscribe(String spuid) { - String masterSpuid = spuids.get(spuid); - - logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); + private Response unsubscribe(String spuid) { + SubscribeProcessorBeans.unsubscribeRequest(); + + synchronized (activeSpus) { + String masterSpuid = spuids.get(spuid); + logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); - if (masterSpuid == null) - return new ErrorResponse(404, "SPUID not found: " + spuid); + if (masterSpuid == null) + return new ErrorResponse(404, "spuid_not_found", "SPUID not found: " + spuid); - // Unregister handler - unregisterHandler(masterSpuid, spuid); + // Unregister handler + unregisterHandler(masterSpuid, spuid); + } return new UnsubscribeResponse(spuid); } - public synchronized void killSpu(String spuid) { - unregisterHandler(spuids.get(spuid), spuid); + public void killSpu(String spuid) { + synchronized (activeSpus) { + unregisterHandler(spuids.get(spuid), spuid); + } } @Override public void notifyEvent(Notification notify) { - synchronized (handlers) { - logger.debug("@notifyEvent: " + notify); + // synchronized (handlers) { + logger.debug("@notifyEvent: " + notify); - String spuid = notify.getSpuid(); + String spuid = notify.getSpuid(); - ArrayList toBeKilled = new ArrayList(); - + ArrayList toBeKilled = new ArrayList(); + + synchronized (activeSpus) { if (activeSpus.containsKey(spuid)) { for (String client : activeSpus.get(spuid)) { try { @@ -141,65 +156,49 @@ public void notifyEvent(Notification notify) { handlers.get(client).notifyEvent(event); sequenceNumbers.put(client, sequenceNumbers.get(client) + 1); } catch (Exception e) { - logger.error("@notifyEvent Client:"+client+" Notification: "+notify+" Exception:"+e.getMessage()); - + logger.error("@notifyEvent Client:" + client + " Notification: " + notify + " Exception:" + + e.getMessage()); + // Handler is gone: unregister it toBeKilled.add(client); } } - + for (String client : toBeKilled) unregisterHandler(spuid, client); } - -// if (activeSpus.get(spuid) == null) { -// // Deactivate SPU -// processor.getSPUManager().deactivate(spuid); -// return; -// } -// -// ArrayList toBeKilled = new ArrayList(); -// for (String client : activeSpus.get(spuid)) { -// if (handlers.get(client) != null) { -// // Dispatching events -// Notification event = new Notification(client, notify.getARBindingsResults(), -// sequenceNumbers.get(client)); -// handlers.get(client).notifyEvent(event); -// sequenceNumbers.put(client, sequenceNumbers.get(client) + 1); -// } else { -// toBeKilled.add(client); -// } -// } -// for (String client : toBeKilled) -// unregisterHandler(spuid, client); } } - private synchronized void registerHandler(String masterSpuid, String spuid, EventHandler handler) { - synchronized (handlers) { - logger.debug("Register SPU handler: " + spuid); + private void registerHandler(String masterSpuid, String spuid, EventHandler handler) { + logger.debug("Register SPU handler: " + spuid); + SubscribeProcessorBeans.registerHandler(); + + synchronized (activeSpus) { if (activeSpus.get(masterSpuid) == null) activeSpus.put(masterSpuid, new ArrayList()); - activeSpus.get(masterSpuid).add(spuid); - + handlers.put(spuid, handler); - sequenceNumbers.put(spuid, 1); spuids.put(spuid, masterSpuid); } } private void unregisterHandler(String masterSpuid, String spuid) { - synchronized (handlers) { - logger.debug("Unregister SPU handler: " + spuid); + logger.debug("Unregister SPU handler: " + spuid); + SubscribeProcessorBeans.unregisterHandler(); + + // SPUids + synchronized (activeSpus) { spuids.remove(spuid); sequenceNumbers.remove(spuid); handlers.remove(spuid); + + if (!activeSpus.containsKey(masterSpuid)) return; - // SPUids activeSpus.get(masterSpuid).remove(spuid); logger.debug(masterSpuid + " number of clients: " + activeSpus.get(masterSpuid).size()); if (activeSpus.get(masterSpuid).isEmpty()) { @@ -290,4 +289,14 @@ public void scale_ns() { public String getUnitScale() { return SubscribeProcessorBeans.getUnitScale(); } + + @Override + public long getSubscribers() { + return SubscribeProcessorBeans.getSubscribers(); + } + + @Override + public long getSubscribers_max() { + return SubscribeProcessorBeans.getSubscribersMax(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java index 9f642687..f99b465b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java @@ -7,7 +7,9 @@ public interface SubscribeProcessingThreadMBean { public long getSPUs_current(); public long getSPUs_max(); - + public long getSubscribers(); + public long getSubscribers_max(); + public float getSPUs_time(); public float getSPUs_time_min(); public float getSPUs_time_max(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index 40830427..1c4d6b2f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -57,9 +57,10 @@ public synchronized Response process(InternalUpdateRequest req) { if (endpointSemaphore != null) try { + //TODO: timeout endpointSemaphore.acquire(); } catch (InterruptedException e) { - return new ErrorResponse(500, e.getMessage()); + return new ErrorResponse(500, "InterruptedException",e.getMessage()); } // Authorized access to the endpoint diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index a3cce5cc..01258d27 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -143,7 +143,7 @@ public synchronized void activate(SPU spu, InternalSubscribeRequest request) { spuid2Request.put(spu.getUUID(), request); SubscribeProcessorBeans.setActiveSPUs(spus.values().size()); - logger.debug(spu.getUUID() + " ACTIVATED (total: " + spus.values().size() + ")"); + logger.debug("ACTIVATE SPU: "+spu.getUUID() + " total (" + spus.values().size() + ")"); } public synchronized void deactivate(String spuid) { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index f04e9984..c0c36bdd 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -119,7 +119,7 @@ public Response processInternal(UpdateResponse update) { if (!added.isEmpty() || !removed.isEmpty()) ret = new Notification(getUUID(), new ARBindingsResults(added, removed)); } catch (Exception e) { - ret = new ErrorResponse(500, e.getMessage()); + ret = new ErrorResponse(500, "Exception: ",e.getMessage()); } return ret; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java index 2aadf1c6..a3db553a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpUtilities.java @@ -37,12 +37,8 @@ public static void sendResponse(HttpAsyncExchange exchange, int httpResponseCode if(!exchange.isCompleted()) exchange.submitResponse(new BasicAsyncResponseProducer(exchange.getResponse())); } - public static void sendFailureResponse(HttpAsyncExchange exchange, int httpResponseCode, - String responseBody) { - - ErrorResponse error = new ErrorResponse(httpResponseCode,responseBody); - - sendResponse(exchange,httpResponseCode, error.toString()); + public static void sendFailureResponse(HttpAsyncExchange exchange, ErrorResponse error) { + sendResponse(exchange,error.getStatusCode(), error.toString()); } public static JsonObject buildEchoResponse(HttpRequest request) { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java index 3b2b7083..1ff9de7a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java @@ -1,8 +1,6 @@ package it.unibo.arces.wot.sepa.engine.protocol.http; import java.io.IOException; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeUnit; import org.apache.http.ExceptionLogger; @@ -52,7 +50,7 @@ public HttpsGate(EngineProperties properties, Scheduler scheduler, Authorization new SecureUpdateHandler(scheduler, oauth)) .registerHandler(properties.getTokenRequestPath(), new JWTRequestHandler(oauth)) .registerHandler("/echo", new EchoHandler()).create(); - } catch (KeyManagementException | NoSuchAlgorithmException | IllegalArgumentException e) { + } catch (IllegalArgumentException e) { throw new SEPASecurityException(e); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java index d380913d..11e807a6 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java @@ -39,7 +39,7 @@ public HttpAsyncRequestConsumer processRequest(HttpRequest request, @Override public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpContext context) throws HttpException, IOException { - logger.debug(">> REQUEST TOKEN"); + logger.info(">> REQUEST TOKEN"); Header[] headers; // Parsing and validating request headers @@ -48,35 +48,35 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont headers = request.getHeaders("Content-Type"); if (headers.length == 0) { logger.error("Content-Type is missing"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "content_type_error","Content-Type is missing")); return; } if (headers.length > 1) { logger.error("Too many Content-Type headers"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, "Too many Content-Type headers"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "content_type_error","Too many Content-Type headers")); return; } if (!headers[0].getValue().equals("application/json")) { logger.error("Content-Type must be: application/json"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, - "Content-Type must be: application/json"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"content_type_error", + "Content-Type must be: application/json")); return; } headers = request.getHeaders("Accept"); if (headers.length == 0) { logger.error("Accept is missing"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, "Accept is missing"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "accept_error","Accept is missing")); return; } if (headers.length > 1) { logger.error("Too many Accept headers"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, "Too many Accept headers"); + HttpUtilities.sendFailureResponse(httpExchange,new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "accept_error","Too many Accept headers")); return; } if (!headers[0].getValue().equals("application/json")) { logger.error("Accept must be: application/json"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, "Accept must be: application/json"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "accept_error","Accept must be: application/json")); return; } @@ -84,7 +84,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont headers = request.getHeaders("Authorization"); if (headers.length != 1) { logger.error("Authorization is missing or multiple"); - HttpUtilities.sendFailureResponse(httpExchange, 401, "Authorization is missing or multiple"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Authorization is missing or multiple")); return; } @@ -93,8 +93,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont if (!basic.startsWith("Basic ")) { logger.error("Authorization must be \"Basic Basic64(:)\""); - HttpUtilities.sendFailureResponse(httpExchange, 401, - "Authorization must be \"Basic Basic64(:)\""); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client","Authorization must be \"Basic Basic64(:)\"")); return; } @@ -106,7 +105,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont if (token.getClass().equals(ErrorResponse.class)) { ErrorResponse error = (ErrorResponse) token; logger.error(token.toString()); - HttpUtilities.sendFailureResponse(httpExchange, error.getErrorCode(), error.getErrorMessage()); + HttpUtilities.sendFailureResponse(httpExchange, error); } else { HttpUtilities.sendResponse(httpExchange, HttpStatus.SC_CREATED, token.toString()); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java index 328786b2..2a1b89c7 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/QueryHandler.java @@ -3,12 +3,15 @@ import java.net.URLDecoder; import java.util.Map; +import org.apache.http.HttpRequest; import org.apache.http.HttpStatus; import org.apache.http.nio.protocol.HttpAsyncExchange; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.commons.response.JWTResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.engine.scheduling.InternalQueryRequest; import it.unibo.arces.wot.sepa.engine.scheduling.InternalUQRequest; @@ -86,4 +89,9 @@ protected InternalUQRequest parse(HttpAsyncExchange exchange) throws SPARQL11Pro throw new SPARQL11ProtocolException(HttpStatus.SC_NOT_FOUND, "Unsupported method: " + exchange.getRequest().getRequestLine().getMethod().toUpperCase()); } + + @Override + protected Response authorize(HttpRequest request) { + return new JWTResponse("Unsecure request is always authorized","authorized",0); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java index bcfd845b..7c2b189a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java @@ -47,7 +47,7 @@ public HttpAsyncRequestConsumer processRequest(HttpRequest request, @Override public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext context) throws HttpException, IOException { - logger.debug(">> REGISTRATION"); + logger.info(">> REGISTRATION"); String name = null; @@ -59,41 +59,39 @@ public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext con Header[] headers = exchange.getRequest().getHeaders("Content-Type"); if (headers.length == 0) { logger.error("Content-Type is missing"); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, "Content-Type is missing"); + HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "content_type_error","Content-Type is missing")); return; } if (headers.length > 1) { logger.error("Too many Content-Type headers"); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, "Too many Content-Type headers"); + HttpUtilities.sendFailureResponse(exchange,new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "content_type_error", "Too many Content-Type headers")); return; } if (!headers[0].getValue().equals("application/json")) { logger.error("Content-Type must be: application/json"); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, - "Content-Type must be: application/json"); + HttpUtilities.sendFailureResponse(exchange,new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "content_type_error","Content-Type must be: application/json")); return; } headers = exchange.getRequest().getHeaders("Accept"); if (headers.length == 0) { logger.error("Accept is missing"); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, "Accept is missing"); + HttpUtilities.sendFailureResponse(exchange,new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "accept_error","Accept is missing")); return; } if (headers.length > 1) { logger.error("Too many Accept headers"); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, "Too many Accept headers"); + HttpUtilities.sendFailureResponse(exchange,new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "accept_error","Too many Accept headers")); return; } if (!headers[0].getValue().equals("application/json")) { logger.error("Accept must be: application/json"); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, - "Accept must be: application/json"); + HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "accept_error","Accept must be: application/json")); return; } } catch (NullPointerException e) { logger.error(e.getMessage()); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, e.getMessage()); + HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "NullPointerException",e.getMessage())); return; } @@ -107,8 +105,8 @@ public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext con HttpEntity entity = ((HttpEntityEnclosingRequest) exchange.getRequest()).getEntity(); try { jsonString = EntityUtils.toString(entity, Charset.forName("UTF-8")); - } catch (ParseException | IOException e) { - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, e.getLocalizedMessage()); + } catch (ParseException e) { + HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "ParseException",e.getMessage())); return; } JsonObject json = new JsonParser().parse(jsonString).getAsJsonObject(); @@ -119,14 +117,14 @@ public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext con // Client credentials if (!json.get("register").getAsJsonObject().get("grant_types").getAsJsonArray().contains(new JsonPrimitive("client_credentials"))) { logger.error("\"grant_types\" must contain \"client_credentials\""); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, - "\"grant_types\" must contain \"client_credentials\""); + HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant", + "\"grant_types\" must contain \"client_credentials\"")); return; } } catch (NullPointerException e) { logger.error(e.getMessage()); - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_BAD_REQUEST, e.getMessage()); + HttpUtilities.sendFailureResponse(exchange,new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "NullPointerException",e.getMessage())); return; } @@ -139,7 +137,7 @@ public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext con ErrorResponse error = (ErrorResponse) cred; logger.error(error.toString()); - HttpUtilities.sendFailureResponse(exchange, error.getErrorCode(), error.getErrorMessage()); + HttpUtilities.sendFailureResponse(exchange, error); return; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java index e67a569a..686bf8e8 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java @@ -22,6 +22,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.bean.HTTPHandlerBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.dependability.CORSManager; @@ -56,18 +58,17 @@ protected boolean validate(HttpRequest request) { return true; } - protected boolean authorize(HttpRequest request) { - // TODO Always authorized - return true; - } + protected abstract Response authorize(HttpRequest request); protected boolean corsHandling(HttpAsyncExchange exchange) { if (!CORSManager.processCORSRequest(exchange)) { - HttpUtilities.sendFailureResponse(exchange, HttpStatus.SC_UNAUTHORIZED, "CORS origin not allowed"); + logger.error("CORS origin not allowed"); + HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "cors_error","CORS origin not allowed")); return false; } if (CORSManager.isPreFlightRequest(exchange)) { + logger.error("Preflight request"); HttpUtilities.sendResponse(exchange, HttpStatus.SC_NO_CONTENT, ""); return false; } @@ -192,7 +193,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont sepaRequest = parse(httpExchange); } catch (SPARQL11ProtocolException e) { logger.error("Parsing failed: " + httpExchange.getRequest()); - HttpUtilities.sendFailureResponse(httpExchange, e.getCode(), "Parsing failed: " + e.getBody()); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "SPARQL11ProtocolException","Parsing failed: " + e.getBody())); jmx.parsingFailed(); return; } @@ -200,17 +201,16 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont // Validate if (!validate(httpExchange.getRequest())) { logger.error("Validation failed SPARQL: " + sepaRequest.getSparql()); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_BAD_REQUEST, - "Validation failed SPARQL: " + sepaRequest.getSparql()); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"sparql_error",sepaRequest.getSparql())); jmx.validatingFailed(); return; } // Authorize - if (!authorize(httpExchange.getRequest())) { - logger.error("Authorization failed SPARQL: " + sepaRequest.getSparql()); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_UNAUTHORIZED, - "Authorization failed SPARQL: " + sepaRequest.getSparql()); + Response oauth = authorize(httpExchange.getRequest()); + if (oauth.isError()) { + logger.error("<< NOT AUTHORIZED: " + httpExchange.getRequest().getLastHeader("Authorization").toString()); + HttpUtilities.sendFailureResponse(httpExchange, (ErrorResponse) oauth); jmx.authorizingFailed(); return; } @@ -220,8 +220,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont ScheduledRequest req = scheduler.schedule(sepaRequest,new SPARQL11ResponseHandler(httpExchange, jmx)); if (req == null) { logger.error("Out of tokens"); - HttpUtilities.sendFailureResponse(httpExchange, HttpStatus.SC_NOT_ACCEPTABLE, - "Too many pending requests"); + HttpUtilities.sendFailureResponse(httpExchange, new ErrorResponse(429,"too_many_requests","Too many pending requests")); jmx.outOfTokens(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java index 7473ae59..30168d34 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java @@ -29,7 +29,7 @@ public SPARQL11ResponseHandler(HttpAsyncExchange httpExchange, HTTPHandlerBeans public void sendResponse(Response response) { if (response.isError()) { ErrorResponse err = (ErrorResponse) response; - HttpUtilities.sendResponse(handler,err.getErrorCode(),response.toString()); + HttpUtilities.sendFailureResponse(handler,err); } else HttpUtilities.sendResponse(handler, HttpStatus.SC_OK, response.toString()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java index 9b477506..9354c784 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java @@ -1,23 +1,67 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; +import org.apache.http.Header; import org.apache.http.HttpRequest; +import org.apache.http.HttpStatus; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureQueryHandler extends QueryHandler implements SecureQueryHandlerMBean { - + private AuthorizationManager am; - + public SecureQueryHandler(Scheduler scheduler, AuthorizationManager am) throws IllegalArgumentException { super(scheduler); - + this.am = am; } - + + /** + * Operation when receiving a HTTP request at a protected endpoint + * +
    +Specific to HTTP request:
    +1. Check if the request contains an Authorization header. 
    +2. Check if the request contains an Authorization: Bearer-header with non-null/empty contents 
    +3. Check if the value of the Authorization: Bearer-header is a JWT object 
    +
    +Token validation:
    +4. Check if the JWT object is signed 
    +5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    +6. Check the contents of the JWT object 
    +7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    +8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    +9. Accept the request as well as "sub" as the originator of the request and process it as usual
    + 
    +Respond with 401 if not
    + 
    +
    + */ @Override - protected boolean authorize(HttpRequest request) { - return am.authorizeRequest(request); + protected Response authorize(HttpRequest request) { + // Extract Bearer authorization + Header[] bearer = request.getHeaders("Authorization"); + + if (bearer.length != 1) { + logger.error("Authorization header is missing or multiple"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request", + "Authorization header must be a single one"); + } + if (!bearer[0].getValue().startsWith("Bearer ")) { + logger.error("Authorization must be \"Bearer JWT\""); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request", + "Authorization header must be \"Bearer JWT\""); + } + + // ****************** + // JWT validation + // ****************** + String jwt = bearer[0].getValue().split(" ")[1]; + + return am.validateToken(jwt); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java index 08b4d67f..f61bdc1b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java @@ -1,24 +1,67 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; +import org.apache.http.Header; import org.apache.http.HttpRequest; +import org.apache.http.HttpStatus; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureUpdateHandler extends UpdateHandler implements SecureUpdateHandlerMBean { - + private AuthorizationManager am; - - public SecureUpdateHandler( - Scheduler scheduler, AuthorizationManager am) throws IllegalArgumentException { + + public SecureUpdateHandler(Scheduler scheduler, AuthorizationManager am) throws IllegalArgumentException { super(scheduler); - + this.am = am; } + /** + * Operation when receiving a HTTP request at a protected endpoint + * +
    +Specific to HTTP request:
    +1. Check if the request contains an Authorization header. 
    +2. Check if the request contains an Authorization: Bearer-header with non-null/empty contents 
    +3. Check if the value of the Authorization: Bearer-header is a JWT object 
    +
    +Token validation:
    +4. Check if the JWT object is signed 
    +5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    +6. Check the contents of the JWT object 
    +7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    +8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    +9. Accept the request as well as "sub" as the originator of the request and process it as usual
    + 
    +Respond with 401 if not
    + 
    +
    + */ @Override - protected boolean authorize(HttpRequest request) { - return am.authorizeRequest(request); + protected Response authorize(HttpRequest request) { + // Extract Bearer authorization + Header[] bearer = request.getHeaders("Authorization"); + + if (bearer.length != 1) { + logger.error("Authorization header is missing or multiple"); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request", + "Authorization header must be a single one"); + } + if (!bearer[0].getValue().startsWith("Bearer ")) { + logger.error("Authorization must be \"Bearer JWT\""); + return new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request", + "Authorization header must be \"Bearer JWT\""); + } + + // ****************** + // JWT validation + // ****************** + String jwt = bearer[0].getValue().split(" ")[1]; + + return am.validateToken(jwt); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java index 0c9ec586..609d4a26 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/UpdateHandler.java @@ -1,10 +1,13 @@ package it.unibo.arces.wot.sepa.engine.protocol.http.handler; +import org.apache.http.HttpRequest; import org.apache.http.HttpStatus; import org.apache.http.nio.protocol.HttpAsyncExchange; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.commons.response.JWTResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.scheduling.InternalUQRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; @@ -25,4 +28,9 @@ protected InternalUQRequest parse(HttpAsyncExchange exchange) { return parsePost(exchange,"update"); } + + @Override + protected Response authorize(HttpRequest request) { + return new JWTResponse("Unsecure request is always authorized","authorized",0); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index f907e0cf..ec7f36d3 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -1,8 +1,5 @@ package it.unibo.arces.wot.sepa.engine.protocol.websocket; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; - import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; @@ -26,8 +23,6 @@ import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureWebsocketServer extends WebsocketServer implements SecureWebsocketServerMBean { @@ -48,12 +43,7 @@ public SecureWebsocketServer(int port, String path, Scheduler scheduler, Authori this.oauth = oauth; - try { - setWebSocketFactory(new DefaultSSLWebSocketServerFactory(oauth.getSSLContext())); - } catch (KeyManagementException | NoSuchAlgorithmException e) { - logger.error(e.getMessage()); - throw new SEPASecurityException(e); - } + setWebSocketFactory(new DefaultSSLWebSocketServerFactory(oauth.getSSLContext())); } @Override @@ -61,85 +51,94 @@ protected InternalRequest parseRequest(String request, WebSocket conn) throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException { JsonObject req; - try { + try{ req = new JsonParser().parse(request).getAsJsonObject(); - - if (req.has("subscribe")) { - String auth = req.get("subscribe").getAsJsonObject().get("authorization").getAsString(); - Response ret = validateToken(auth); - if (ret.isError()) { - // Not authorized - WebsocketBeans.onNotAuthorizedRequest(); - - logger.warn("NOT AUTHORIZED"); - conn.send(ret.toString()); - return null; - } - - String sparql = null; - String alias = null; - String defaultGraphUri = null; - String namedGraphUri = null; - - try { - sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); - } - catch(Exception e) { - logger.error("SPARQL member not found"); - return null; - } - - try { - alias = req.get("subscribe").getAsJsonObject().get("alias").getAsString(); - } - catch(Exception e) {} - - try { - defaultGraphUri = req.get("subscribe").getAsJsonObject().get("default-graph-uri").getAsString(); - } - catch(Exception e) {} - - try { - namedGraphUri = req.get("subscribe").getAsJsonObject().get("named-graph-uri").getAsString(); - } - catch(Exception e) {} - - return new InternalSubscribeRequest(sparql,alias,defaultGraphUri,namedGraphUri,activeSockets.get(conn)); - } - else if (req.has("unsubscribe")) { - Response ret = validateToken( - req.get("unsubscribe").getAsJsonObject().get("authorization").getAsString()); - if (ret.isError()) { - // Not authorized - WebsocketBeans.onNotAuthorizedRequest(); - - logger.warn("NOT AUTHORIZED"); - conn.send(ret.toString()); - return null; - } - return new InternalUnsubscribeRequest(req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString()); - } - } catch (Exception e) { - logger.debug(e.getLocalizedMessage()); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); - conn.send(response.toString()); + } + catch(Exception e) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","Exception: " + request); + conn.send(error.toString()); + logger.error(error); return null; } - logger.debug("Unknown request: "+request); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Unknown request: "+request); - conn.send(response.toString()); - return null; - } + // CHECK AUTHORIZATION + Response ret = validateRequest(req); + + if (ret.isError()) { + // Not authorized + WebsocketBeans.onNotAuthorizedRequest(); - private Response validateToken(String bearer) { - String jwt = null; - try { - if (!bearer.startsWith("Bearer ")) - new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Authorization value MUST be of type Bearer"); - jwt = bearer.substring(7); - } catch (Exception e) { - return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "Authorization key value is wrong"); + logger.warn("NOT AUTHORIZED"); + conn.send(ret.toString()); + return null; + } + + return super.parseRequest(request, conn); + } +/** +
    +	Specific to SPARQL 1.1 SE Subscribe request:
    +	1. Check if the request contains an "authorization" member. 
    +	2. Check if the request contains an "authorization" member that start with "Bearer" 
    +	3. Check if the value of the "authorization" member is a JWT object ==> VALIDATE TOKEN
    +
    +	Token validation:
    +	4. Check if the JWT object is signed 
    +	5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    +	6. Check the contents of the JWT object 
    +	7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    +	8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    +	9. Accept the request as well as "sub" as the originator of the request and process it as usual
    +	 
    +	Respond with 401 if not
    +	 
    +	
    + */ + private Response validateRequest(JsonObject request) { + String bearer = null; + JsonObject subUnsub = null; + + if (request.has("subscribe")) subUnsub = request.get("subscribe").getAsJsonObject(); + else if (request.has("unsubscribe")) subUnsub = request.get("unsubscribe").getAsJsonObject(); + + if (subUnsub == null) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request","Neither subscribe or unsuscribe found"); + logger.error(error); + return error; + } + + if (!subUnsub.has("authorization")) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization member is missing"); + logger.error(error); + return error; + } + + try{ + bearer = subUnsub.get("authorization").getAsString(); + } + catch(Exception e) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization member is not a string"); + logger.error(error); + return error; + } + + if (!bearer.startsWith("Bearer ")) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization value MUST be of type Bearer"); + logger.error(error); + return error; + } + + String jwt = bearer.substring(7); + + if (jwt == null) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Token is null"); + logger.error(error); + return error; + } + if (jwt.equals("")) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Token is empty"); + logger.error(error); + return error; } // Token validation diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java index 0bc3cf6c..a8d4b055 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java @@ -11,6 +11,7 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; +import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; @@ -43,12 +44,15 @@ public void sendResponse(Response response) throws SEPAProtocolException { logger.trace(response); if (response.isSubscribeResponse()) { + WebsocketBeans.subscribeResponse(); dependabilityMng.onSubscribe(socket.hashCode(), ((SubscribeResponse)response).getSpuid()); } else if (response.isUnsubscribeResponse()) { + WebsocketBeans.unsubscribeResponse(); dependabilityMng.onUnsubscribe(socket.hashCode(), ((UnsubscribeResponse)response).getSpuid()); } else if (response.isError()) { + WebsocketBeans.errorResponse(); logger.error(response); dependabilityMng.onError(socket.hashCode(), (ErrorResponse)response); } @@ -58,6 +62,8 @@ else if (response.isError()) { @Override public void notifyEvent(Notification notify) throws SEPAProtocolException { + WebsocketBeans.notification(); + send(notify); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index 23e9d0f4..bf1c2c47 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -83,11 +83,11 @@ public WebsocketServer(int port, String path, Scheduler scheduler, Dependability @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { - logger.trace("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); + logger.debug("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, + ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND,"wrong_path", "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); conn.send(response.toString()); return; @@ -120,7 +120,7 @@ public void onMessage(WebSocket conn, String message) { if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, + ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND,"wrong_path", "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); conn.send(response.toString()); return; @@ -145,7 +145,7 @@ public void onMessage(WebSocket conn, String message) { ScheduledRequest request = scheduler.schedule(req, activeSockets.get(conn)); if (request == null) { logger.error("Out of tokens"); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_ACCEPTABLE, + ErrorResponse response = new ErrorResponse(429,"too_many_requests", "Too many pending requests"); conn.send(response.toString()); } @@ -177,8 +177,9 @@ protected InternalRequest parseRequest(String request, WebSocket conn) try { req = new JsonParser().parse(request).getAsJsonObject(); } catch (JsonParseException e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException","JsonParseException: " + request); conn.send(error.toString()); + logger.error(error); return null; } @@ -191,8 +192,9 @@ protected InternalRequest parseRequest(String request, WebSocket conn) try { sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); } catch (Exception e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "sparql member not found: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","sparql member not found: " + request); conn.send(error.toString()); + logger.error(error); return null; } @@ -217,7 +219,7 @@ protected InternalRequest parseRequest(String request, WebSocket conn) try { spuid = req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString(); } catch (Exception e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "spuid member not found: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","spuid member not found: " + request); conn.send(error.toString()); return null; } @@ -225,7 +227,7 @@ protected InternalRequest parseRequest(String request, WebSocket conn) return new InternalUnsubscribeRequest(spuid); } - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Bad request: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unsupported","Bad request: " + request); conn.send(error.toString()); return null; } @@ -303,4 +305,24 @@ public long getFragmented() { public long getErrors() { return WebsocketBeans.getErrors(); } + + @Override + public long getErrorResponses() { + return WebsocketBeans.getErrorResponses(); + } + + @Override + public long getSubscribeResponse() { + return WebsocketBeans.getSubscribeResponses(); + } + + @Override + public long getUnsubscribeResponse() { + return WebsocketBeans.getUnsubscribeResponses(); + } + + @Override + public long getNotifications() { + return WebsocketBeans.getNotifications(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServerMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServerMBean.java index ed5f98f5..9fffd4ba 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServerMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServerMBean.java @@ -8,4 +8,12 @@ public interface WebsocketServerMBean { public long getFragmented(); public long getErrors(); + + public long getErrorResponses(); + + public long getSubscribeResponse(); + + public long getUnsubscribeResponse(); + + public long getNotifications(); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index d3132407..01534de4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -59,6 +59,7 @@ public Scheduler(EngineProperties properties) { // JMX SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SchedulerBeans.setQueueSize(properties.getSchedulingQueueSize()); + SchedulerBeans.setTimeout(properties.getSchedulerTimeout()); setName("SEPA-Scheduler"); } @@ -79,7 +80,7 @@ public synchronized ScheduledRequest schedule(InternalRequest request, ResponseH return null; } - logger.debug(scheduled); + logger.info(">> "+scheduled); // Register response handler responders.put(scheduled.getToken(), handler); @@ -97,8 +98,7 @@ public void run() { try { // Wait for response ScheduledResponse response = queue.waitResponse(); - - logger.debug(response); + logger.info("<< "+response); // The token int token = response.getToken(); @@ -159,4 +159,14 @@ public int getQueueSize() { public SchedulerQueue getSchedulerQueue() { return queue; } + + @Override + public int getTimeout() { + return SchedulerBeans.getTimeout(); + } + + @Override + public void setTimeout(int timeout) { + SchedulerBeans.setTimeout(timeout); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerMBean.java index 350969ae..61017d2a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerMBean.java @@ -16,4 +16,8 @@ public interface SchedulerMBean { public void reset(); public int getQueueSize(); + + public int getTimeout(); + + public void setTimeout(int timeout); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java index 69230998..0dd8c9c3 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java @@ -14,18 +14,18 @@ public class SchedulerQueue { private static final Logger logger = LogManager.getLogger(); // Tokens - private final Vector tokens = new Vector(); + private final static Vector tokens = new Vector(); // Requests - private final LinkedBlockingQueue updates = new LinkedBlockingQueue(); - private final LinkedBlockingQueue queries = new LinkedBlockingQueue(); - private final LinkedBlockingQueue subscribesUnsubscribes = new LinkedBlockingQueue(); + private final static LinkedBlockingQueue updates = new LinkedBlockingQueue(); + private final static LinkedBlockingQueue queries = new LinkedBlockingQueue(); + private final static LinkedBlockingQueue subscribesUnsubscribes = new LinkedBlockingQueue(); // Broken subscriptions - private final LinkedBlockingQueue toBeKilled = new LinkedBlockingQueue(); + private final static LinkedBlockingQueue toBeKilled = new LinkedBlockingQueue(); // Responses - private final LinkedBlockingQueue responses = new LinkedBlockingQueue(); + private final static LinkedBlockingQueue responses = new LinkedBlockingQueue(); public SchedulerQueue(long size) { // Initialize token jar @@ -74,11 +74,12 @@ private synchronized void releaseToken(Integer token) { } } - public synchronized ScheduledRequest addRequest(InternalRequest req,ResponseHandler handler) { + public ScheduledRequest addRequest(InternalRequest req,ResponseHandler handler) { int token = getToken(); if (token == -1) return null; ScheduledRequest request = new ScheduledRequest(token,req,handler); + if (req.isUpdateRequest()) updates.add(request); else if (req.isQueryRequest()) queries.add(request); else if (req.isSubscribeRequest()) subscribesUnsubscribes.add(request); @@ -98,18 +99,17 @@ public ScheduledRequest waitQueryRequest() throws InterruptedException { public ScheduledRequest waitSubscribeUnsubscribeRequest() throws InterruptedException { return subscribesUnsubscribes.take(); } - - public synchronized void addResponse(int token,Response res) { - responses.offer(new ScheduledResponse(token,res)); - } - + public ScheduledResponse waitResponse() throws InterruptedException { - ScheduledResponse ret = responses.take(); - releaseToken(ret.getToken()); - return ret; + return responses.take(); } - public synchronized void killSpuid(String spuid) { + public void addResponse(int token,Response res) { + releaseToken(token); + responses.offer(new ScheduledResponse(token,res)); + } + + public void killSpuid(String spuid) { toBeKilled.offer(spuid); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java index 87a6f747..1a8d92e2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/timing/Timings.java @@ -15,7 +15,7 @@ public static long getTime() { } public synchronized static void log(String tag,long start,long stop) { - String message = String.format("%d,%s,%d",System.currentTimeMillis(),tag,stop-start); + String message = String.format("%d,%d,%s",System.currentTimeMillis(),stop-start,tag); logger.log(Level.getLevel("timing"),message); } diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index be72e020..639d3c4c 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -15,7 +15,7 @@ DEBUG 500 TRACE 600 ALL Integer.MAX_VALUE --> - + @@ -26,8 +26,8 @@ ALL Integer.MAX_VALUE - - + + diff --git a/tools/arces-demo.jsap b/tools/arces-demo.jsap index 8eefa493..73c9ad69 100644 --- a/tools/arces-demo.jsap +++ b/tools/arces-demo.jsap @@ -539,6 +539,15 @@ } } }, + "UPDATE_OBSERVATION_LABEL": { + "sparql": "DELETE {?observation rdfs:label ?oldLabel} INSERT {?observation rdfs:label ?label} WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?oldLabel}", + "forcedBindings": { + "observation": { + "type": "xsd:string", + "value": "arces-monitor:ObservationXYZ" + } + } + }, "ADD_OBSERVATION": { "sparql": "INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue 'NaN'} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity)}", "forcedBindings": { diff --git a/tools/endpoint.jpar b/tools/endpoint.jpar new file mode 100644 index 00000000..08e92a9f --- /dev/null +++ b/tools/endpoint.jpar @@ -0,0 +1 @@ +{"ambiental":{"/ffa574972ab9/applink/107/001BC50C700009CD":{"observation":"arces-monitor:001BC50C700009CD-DASH7-Temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Star","comment":"Temperature sensor used to show the need of a new air conditioning system in SEHM lab","label":"Temperature SEHM lab"},"/ffa574972ab9/applink/107/001BC50C700009BB":{"observation":"arces-monitor:001BC50C700009BB-DASH7-Temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Star","comment":"emperature sensor used to show the need of a new air conditioning system in ST office","label":"Temperature ST office"},"5CCF7F15676D/temperature":{"observation":"arces-monitor:5CCF7F15676D-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura zona rack sala server Toffano","label":"Temperatura zona rack"},"5CCF7F15676D/humidity":{"observation":"arces-monitor:5CCF7F15676D-humidity","unit":"qudt-unit-1-1:Percent","location":"arces-monitor:Mars","comment":"Umidità zona rack sala server Toffano","label":"Umidità zona rack"},"5CCF7F1B599E/temperature":{"observation":"arces-monitor:5CCF7F1B599E-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura zona finestra sala server Toffano","label":"Temperatura zona finestra"},"5CCF7F1B599E/humidity":{"observation":"arces-monitor:5CCF7F1B599E-humidity","unit":"qudt-unit-1-1:Percent","location":"arces-monitor:Mars","comment":"Umidità zona finestra sala server Toffano","label":"Umidità zona finestra"},"5CCF7F151DC9/temperature":{"observation":"arces-monitor:5CCF7F151DC9-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura esterna sala server Toffano","label":"Temperatura esterna"},"5CCF7F1B58AC/temperature":{"observation":"arces-monitor:5CCF7F1B58AC-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Star","comment":"Temperatura zona rack sala server Pepoli","label":"Temperatura zona rack"},"5CCF7F1B58AC/humidity":{"observation":"arces-monitor:5CCF7F1B58AC-humidity","unit":"qudt-unit-1-1:Percent","location":"arces-monitor:Star","comment":"Umidità zona rack sala server Pepoli","label":"Umidità zona rack"}},"server":{"samba":{"arces/servers/mars/marsamba/hd/sda/temperature":{"observation":"arces-monitor:ServerSambaHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server SAMBA HDD SDA","label":"Temperatura Server SAMBA HDD SDA"},"arces/servers/mars/marsamba/hd/sdb/temperature":{"observation":"arces-monitor:ServerSambaHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server SAMBA HDD SDB","label":"Temperatura Server SAMBA HDD SDB"}},"giove":{"arces/servers/mars/giove/hd/sda/temperature":{"observation":"arces-monitor:ServerGioveHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDA","label":"Temperatura Server GIOVE HDD SDA"},"arces/servers/mars/giove/hd/sdb/temperature":{"observation":"arces-monitor:ServerGioveHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDB","label":"Temperatura Server GIOVE HDD SDB"},"arces/servers/mars/giove/hd/sdc/temperature":{"observation":"arces-monitor:ServerGioveHDDSdc","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDC","label":"Temperatura Server GIOVE HDD SDC"},"arces/servers/mars/giove/hd/sdd/temperature":{"observation":"arces-monitor:ServerGioveHDDSdd","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDD","label":"Temperatura Server GIOVE HDD SDD"},"arces/servers/mars/giove/hd/sdf/temperature":{"observation":"arces-monitor:ServerGioveHDDSdf","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDF","label":"Temperatura Server GIOVE HDD SDF"},"arces/servers/mars/giove/hd/sdg/temperature":{"observation":"arces-monitor:ServerGioveHDDSdg","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDG","label":"Temperatura Server GIOVE HDD SDG"},"arces/servers/mars/giove/hd/sdh/temperature":{"observation":"arces-monitor:ServerGioveHDDSdh","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDH","label":"Temperatura Server GIOVE HDD SDH"},"arces/servers/mars/giove/cpu/core-6/temperature":{"observation":"arces-monitor:ServerGiove6","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 6","label":"Temperatura Server GIOVE Core 6"},"arces/servers/mars/giove/cpu/core-5/temperature":{"observation":"arces-monitor:ServerGiove5","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 5","label":"Temperatura Server GIOVE Core 5"},"arces/servers/mars/giove/cpu/core-4/temperature":{"observation":"arces-monitor:ServerGiove4","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 4","label":"Temperatura Server GIOVE Core 4"},"arces/servers/mars/giove/cpu/core-3/temperature":{"observation":"arces-monitor:ServerGiove3","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 3","label":"Temperatura Server GIOVE Core 3"},"arces/servers/mars/giove/cpu/core-2/temperature":{"observation":"arces-monitor:ServerGiove2","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 2","label":"Temperatura Server GIOVE Core 2"},"arces/servers/mars/giove/cpu/core-1/temperature":{"observation":"arces-monitor:ServerGiove1","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 1","label":"Temperatura Server GIOVE Core 1"}},"mml":{"arces/servers/mars/mml/hd/sda/temperature":{"observation":"arces-monitor:ServerMmlHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML HDD SDA","label":"Temperatura Server MML HDD SDA"},"arces/servers/mars/mml/hd/sdb/temperature":{"observation":"arces-monitor:ServerMmlHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML HDD SDB","label":"Temperatura Server MML HDD SDB"},"arces/servers/mars/mml/cpu/core-6/temperature":{"observation":"arces-monitor:ServerMml6","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 6","label":"Temperatura Server MML Core 6"},"arces/servers/mars/mml/cpu/core-5/temperature":{"observation":"arces-monitor:ServerMml5","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 5","label":"Temperatura Server MML Core 5"},"arces/servers/mars/mml/cpu/core-4/temperature":{"observation":"arces-monitor:ServerMml4","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 4","label":"Temperatura Server MML Core 4"},"arces/servers/mars/mml/cpu/core-3/temperature":{"observation":"arces-monitor:ServerMml3","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 3","label":"Temperatura Server MML Core 3"},"arces/servers/mars/mml/cpu/core-2/temperature":{"observation":"arces-monitor:ServerMml2","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 2","label":"Temperatura Server MML Core 2"},"arces/servers/mars/mml/cpu/core-1/temperature":{"observation":"arces-monitor:ServerMml1","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 1","label":"Temperatura Server MML Core 1"}},"ercole":{"arces/servers/ares/ercole/cpu/core-20/temperature":{"observation":"arces-monitor:ServerErcoleCore20","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 20","label":"Temperatura Server ERCOLE Core 20"},"arces/servers/ares/ercole/cpu/core-19/temperature":{"observation":"arces-monitor:ServerErcoleCore19","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 19","label":"Temperatura Server ERCOLE Core 19"},"arces/servers/ares/ercole/cpu/core-18/temperature":{"observation":"arces-monitor:ServerErcoleCore18","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 18","label":"Temperatura Server ERCOLE Core 18"},"arces/servers/ares/ercole/cpu/core-17/temperature":{"observation":"arces-monitor:ServerErcoleCore17","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 17","label":"Temperatura Server ERCOLE Core 17"},"arces/servers/ares/ercole/cpu/core-16/temperature":{"observation":"arces-monitor:ServerErcoleCore16","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 16","label":"Temperatura Server ERCOLE Core 16"},"arces/servers/ares/ercole/cpu/core-15/temperature":{"observation":"arces-monitor:ServerErcoleCore15","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 15","label":"Temperatura Server ERCOLE Core 15"},"arces/servers/ares/ercole/cpu/core-14/temperature":{"observation":"arces-monitor:ServerErcoleCore14","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 14","label":"Temperatura Server ERCOLE Core 14"},"arces/servers/ares/ercole/cpu/core-13/temperature":{"observation":"arces-monitor:ServerErcoleCore13","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 13","label":"Temperatura Server ERCOLE Core 13"},"arces/servers/ares/ercole/cpu/core-12/temperature":{"observation":"arces-monitor:ServerErcoleCore12","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 12","label":"Temperatura Server ERCOLE Core 12"},"arces/servers/ares/ercole/cpu/core-11/temperature":{"observation":"arces-monitor:ServerErcoleCore11","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 11","label":"Temperatura Server ERCOLE Core 11"},"arces/servers/ares/ercole/cpu/core-10/temperature":{"observation":"arces-monitor:ServerErcoleCore10","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 10","label":"Temperatura Server ERCOLE Core 10"},"arces/servers/ares/ercole/cpu/core-9/temperature":{"observation":"arces-monitor:ServerErcoleCore9","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 9","label":"Temperatura Server ERCOLE Core 9"},"arces/servers/ares/ercole/cpu/core-8/temperature":{"observation":"arces-monitor:ServerErcoleCore8","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 8","label":"Temperatura Server ERCOLE Core 8"},"arces/servers/ares/ercole/cpu/core-7/temperature":{"observation":"arces-monitor:ServerErcoleCore7","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 7","label":"Temperatura Server ERCOLE Core 7"},"arces/servers/ares/ercole/cpu/core-6/temperature":{"observation":"arces-monitor:ServerErcoleCore6","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 6","label":"Temperatura Server ERCOLE Core 6"},"arces/servers/ares/ercole/cpu/core-5/temperature":{"observation":"arces-monitor:ServerErcoleCore5","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 5","label":"Temperatura Server ERCOLE Core 5"},"arces/servers/ares/ercole/cpu/core-4/temperature":{"observation":"arces-monitor:ServerErcoleCore4","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 4","label":"Temperatura Server ERCOLE Core 4"},"arces/servers/ares/ercole/cpu/core-3/temperature":{"observation":"arces-monitor:ServerErcoleCore3","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 3","label":"Temperatura Server ERCOLE Core 3"},"arces/servers/ares/ercole/cpu/core-2/temperature":{"observation":"arces-monitor:ServerErcoleCore2","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 2","label":"Temperatura Server ERCOLE Core 2"},"arces/servers/ares/ercole/cpu/core-1/temperature":{"observation":"arces-monitor:ServerErcoleCore1","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 1","label":"Temperatura Server ERCOLE Core 1"},"arces/servers/ares/ercole/hd/sda/temperature":{"observation":"arces-monitor:ServerErcoleHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE HDD SDA","label":"Temperatura Server ERCOLE HDD SDA"},"arces/servers/ares/ercole/hd/sdb/temperature":{"observation":"arces-monitor:ServerErcoleHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE HDD SDB","label":"Temperatura Server ERCOLE HDD SDB"}}},"host":"localhost","sparql11protocol":{"protocol":"http","port":9999,"query":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"},"update":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"ws","availableProtocols":{"ws":{"port":9443,"path":"/subscribe"},"wss":{}}}} \ No newline at end of file diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java index 18305397..5a7e85fd 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/tools/Dashboard.java @@ -228,7 +228,7 @@ public void onBrokenConnection() { @Override public void onError(ErrorResponse errorResponse) { - logger.error(errorResponse.getErrorMessage()); + logger.error(errorResponse); } @Override @@ -1672,7 +1672,7 @@ else if (type.toUpperCase().equals("BNODE")) Instant stop = Instant.now(); if (ret.isError()) { logger.error(ret.toString() + String.format(" (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); - queryInfo.setText("Error: " + ((ErrorResponse) ret).getErrorCode()); + queryInfo.setText("Error: " + ((ErrorResponse) ret).getStatusCode()); } else { QueryResponse results = (QueryResponse) ret; logger.info(String.format("Results: %d (%d ms)", results.getBindingsResults().size(), @@ -1708,7 +1708,7 @@ else if (type.equals("BNODE")) Instant stop = Instant.now(); if (ret.isError()) { logger.error(ret.toString() + String.format(" (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); - updateInfo.setText("Error: " + ((ErrorResponse) ret).getErrorCode()); + updateInfo.setText("Error: " + ((ErrorResponse) ret).getStatusCode()); } else { logger.info(String.format("Update OK (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); updateInfo.setText(String.format("Update OK (%d ms)", (stop.toEpochMilli() - start.toEpochMilli()))); From ddc253b87ad43e322c474ec758a5016b82062743 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 6 Sep 2018 13:09:12 +0200 Subject: [PATCH 36/76] Chat test removed --- .../commons/protocol/SPARQL11Protocol.java | 5 +- tools/.gitignore | 1 + tools/arces-demo.jsap | 314 +----------------- .../wot/sepa/apps/chat/SEPAChatTest.java | 19 +- .../arces/wot/sepa/apps/mqtt/MQTTAdapter.java | 31 +- .../arces/wot/sepa/apps/mqtt/MQTTMapper.java | 12 + .../wot/sepa/apps/mqtt/MQTTSmartifier.java | 223 ++++++++----- .../wot/sepa/apps/mqtt/ObservationLogger.java | 28 +- tools/src/main/resources/log4j2.xml | 2 +- .../sepa/apps/chat/ConfigurationProvider.java | 22 -- 10 files changed, 221 insertions(+), 436 deletions(-) rename tools/src/{test => main}/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java (86%) delete mode 100644 tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index c3ac84dc..8eab6ec6 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -81,10 +81,9 @@ public class SPARQL11Protocol implements java.io.Closeable { protected final SEPASecurityManager sm; public SPARQL11Protocol(SEPASecurityManager sm) { - if (sm == null) - throw new IllegalArgumentException("Security manager is null"); this.sm = sm; - httpClient = sm.getSSLHttpClient(); + if (sm == null) httpClient = HttpClients.createDefault(); + else httpClient = sm.getSSLHttpClient(); } public SPARQL11Protocol() { diff --git a/tools/.gitignore b/tools/.gitignore index 341767ec..7e0f4ace 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -1,3 +1,4 @@ /dashboard.properties /defaults.jpar /logs/ +/arces-demo-ttn.jsap diff --git a/tools/arces-demo.jsap b/tools/arces-demo.jsap index 73c9ad69..f0573432 100644 --- a/tools/arces-demo.jsap +++ b/tools/arces-demo.jsap @@ -84,378 +84,98 @@ "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Star", "comment": "Temperature sensor used to show the need of a new air conditioning system in SEHM lab", - "label": "Temperature SEHM lab" + "label": "SEHM lab temperature (ARCES, Pepoli)" }, "/ffa574972ab9/applink/107/001BC50C700009BB": { "observation": "arces-monitor:001BC50C700009BB-DASH7-Temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Star", - "comment": "emperature sensor used to show the need of a new air conditioning system in ST office", - "label": "Temperature ST office" - }, - "arces/servers/ares/ercole/cpu/core-20/temperature": { - "observation": "arces-monitor:ServerErcoleCore20", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 20", - "label": "Temperatura Server ERCOLE Core 20" - }, - "arces/servers/ares/ercole/cpu/core-19/temperature": { - "observation": "arces-monitor:ServerErcoleCore19", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 19", - "label": "Temperatura Server ERCOLE Core 19" - }, - "arces/servers/ares/ercole/cpu/core-18/temperature": { - "observation": "arces-monitor:ServerErcoleCore18", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 18", - "label": "Temperatura Server ERCOLE Core 18" - }, - "arces/servers/ares/ercole/cpu/core-17/temperature": { - "observation": "arces-monitor:ServerErcoleCore17", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 17", - "label": "Temperatura Server ERCOLE Core 17" - }, - "arces/servers/ares/ercole/cpu/core-16/temperature": { - "observation": "arces-monitor:ServerErcoleCore16", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 16", - "label": "Temperatura Server ERCOLE Core 16" - }, - "arces/servers/ares/ercole/cpu/core-15/temperature": { - "observation": "arces-monitor:ServerErcoleCore15", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 15", - "label": "Temperatura Server ERCOLE Core 15" - }, - "arces/servers/ares/ercole/cpu/core-14/temperature": { - "observation": "arces-monitor:ServerErcoleCore14", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 14", - "label": "Temperatura Server ERCOLE Core 14" - }, - "arces/servers/ares/ercole/cpu/core-13/temperature": { - "observation": "arces-monitor:ServerErcoleCore13", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 13", - "label": "Temperatura Server ERCOLE Core 13" - }, - "arces/servers/ares/ercole/cpu/core-12/temperature": { - "observation": "arces-monitor:ServerErcoleCore12", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 12", - "label": "Temperatura Server ERCOLE Core 12" - }, - "arces/servers/ares/ercole/cpu/core-11/temperature": { - "observation": "arces-monitor:ServerErcoleCore11", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 11", - "label": "Temperatura Server ERCOLE Core 11" - }, - "arces/servers/ares/ercole/cpu/core-10/temperature": { - "observation": "arces-monitor:ServerErcoleCore10", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 10", - "label": "Temperatura Server ERCOLE Core 10" - }, - "arces/servers/ares/ercole/cpu/core-9/temperature": { - "observation": "arces-monitor:ServerErcoleCore9", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 9", - "label": "Temperatura Server ERCOLE Core 9" - }, - "arces/servers/ares/ercole/cpu/core-8/temperature": { - "observation": "arces-monitor:ServerErcoleCore8", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 8", - "label": "Temperatura Server ERCOLE Core 8" - }, - "arces/servers/ares/ercole/cpu/core-7/temperature": { - "observation": "arces-monitor:ServerErcoleCore7", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 7", - "label": "Temperatura Server ERCOLE Core 7" - }, - "arces/servers/ares/ercole/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerErcoleCore6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 6", - "label": "Temperatura Server ERCOLE Core 6" - }, - "arces/servers/ares/ercole/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerErcoleCore5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 5", - "label": "Temperatura Server ERCOLE Core 5" - }, - "arces/servers/ares/ercole/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerErcoleCore4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 4", - "label": "Temperatura Server ERCOLE Core 4" - }, - "arces/servers/ares/ercole/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerErcoleCore3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 3", - "label": "Temperatura Server ERCOLE Core 3" - }, - "arces/servers/ares/ercole/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerErcoleCore2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 2", - "label": "Temperatura Server ERCOLE Core 2" + "comment": "Temperature sensor used to show the need of a new air conditioning system in ST office", + "label": "ST office temperature (ARCES, Pepoli)" }, "arces/servers/ares/ercole/cpu/core-1/temperature": { "observation": "arces-monitor:ServerErcoleCore1", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE Core 1", - "label": "Temperatura Server ERCOLE Core 1" - }, - "arces/servers/mars/mml/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerMml6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 6", - "label": "Temperatura Server MML Core 6" - }, - "arces/servers/mars/mml/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerMml5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 5", - "label": "Temperatura Server MML Core 5" - }, - "arces/servers/mars/mml/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerMml4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 4", - "label": "Temperatura Server MML Core 4" - }, - "arces/servers/mars/mml/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerMml3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 3", - "label": "Temperatura Server MML Core 3" - }, - "arces/servers/mars/mml/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerMml2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 2", - "label": "Temperatura Server MML Core 2" - }, - "arces/servers/mars/mml/cpu/core-1/temperature": { - "observation": "arces-monitor:ServerMml1", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML Core 1", - "label": "Temperatura Server MML Core 1" - }, - "arces/servers/mars/giove/cpu/core-6/temperature": { - "observation": "arces-monitor:ServerGiove6", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 6", - "label": "Temperatura Server GIOVE Core 6" - }, - "arces/servers/mars/giove/cpu/core-5/temperature": { - "observation": "arces-monitor:ServerGiove5", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 5", - "label": "Temperatura Server GIOVE Core 5" - }, - "arces/servers/mars/giove/cpu/core-4/temperature": { - "observation": "arces-monitor:ServerGiove4", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 4", - "label": "Temperatura Server GIOVE Core 4" - }, - "arces/servers/mars/giove/cpu/core-3/temperature": { - "observation": "arces-monitor:ServerGiove3", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 3", - "label": "Temperatura Server GIOVE Core 3" - }, - "arces/servers/mars/giove/cpu/core-2/temperature": { - "observation": "arces-monitor:ServerGiove2", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE Core 2", - "label": "Temperatura Server GIOVE Core 2" + "comment": "Temperature Server ERCOLE Core 1 of 20 (ARCES, Risorgimento)", + "label": "Server ERCOLE Core 1 of 20 temperature (ARCES, Risorgimento)" }, "arces/servers/mars/giove/cpu/core-1/temperature": { "observation": "arces-monitor:ServerGiove1", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Mars", "comment": "Temperatura Server GIOVE Core 1", - "label": "Temperatura Server GIOVE Core 1" - }, - "arces/servers/mars/mml/hd/sda/temperature": { - "observation": "arces-monitor:ServerMmlHDDSda", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML HDD SDA", - "label": "Temperatura Server MML HDD SDA" - }, - "arces/servers/mars/mml/hd/sdb/temperature": { - "observation": "arces-monitor:ServerMmlHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server MML HDD SDB", - "label": "Temperatura Server MML HDD SDB" + "label": "Server GIOVE Core 1 of 6 temperature (ARCES, Toffano)" }, "arces/servers/mars/giove/hd/sda/temperature": { "observation": "arces-monitor:ServerGioveHDDSda", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDA", - "label": "Temperatura Server GIOVE HDD SDA" - }, - "arces/servers/mars/giove/hd/sdb/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDB", - "label": "Temperatura Server GIOVE HDD SDB" - }, - "arces/servers/mars/giove/hd/sdc/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdc", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDC", - "label": "Temperatura Server GIOVE HDD SDC" - }, - "arces/servers/mars/giove/hd/sdd/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdd", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDD", - "label": "Temperatura Server GIOVE HDD SDD" - }, - "arces/servers/mars/giove/hd/sdf/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdf", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDF", - "label": "Temperatura Server GIOVE HDD SDF" - }, - "arces/servers/mars/giove/hd/sdg/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdg", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDG", - "label": "Temperatura Server GIOVE HDD SDG" - }, - "arces/servers/mars/giove/hd/sdh/temperature": { - "observation": "arces-monitor:ServerGioveHDDSdh", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server GIOVE HDD SDH", - "label": "Temperatura Server GIOVE HDD SDH" + "comment": "Temperature Server GIOVE HDD SDA", + "label": "Server GIOVE HDD 1 of 7 temperature (ARCES, Toffano)" }, "arces/servers/mars/marsamba/hd/sda/temperature": { "observation": "arces-monitor:ServerSambaHDDSda", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Mars", "comment": "Temperatura Server SAMBA HDD SDA", - "label": "Temperatura Server SAMBA HDD SDA" - }, - "arces/servers/mars/marsamba/hd/sdb/temperature": { - "observation": "arces-monitor:ServerSambaHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Mars", - "comment": "Temperatura Server SAMBA HDD SDB", - "label": "Temperatura Server SAMBA HDD SDB" + "label": "Server SAMBA HDD 1 of 2 temperature (ARCES, Toffano)" }, "arces/servers/ares/ercole/hd/sda/temperature": { "observation": "arces-monitor:ServerErcoleHDDSda", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Ares", "comment": "Temperatura Server ERCOLE HDD SDA", - "label": "Temperatura Server ERCOLE HDD SDA" - }, - "arces/servers/ares/ercole/hd/sdb/temperature": { - "observation": "arces-monitor:ServerErcoleHDDSdb", - "unit": "qudt-unit-1-1:DegreeCelsius", - "location": "arces-monitor:Ares", - "comment": "Temperatura Server ERCOLE HDD SDB", - "label": "Temperatura Server ERCOLE HDD SDB" + "label": "Server ERCOLE HDD 1 of 2 temperature (ARCES, Risorgimento)" }, "5CCF7F15676D/temperature": { "observation": "arces-monitor:5CCF7F15676D-temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Mars", "comment": "Temperatura zona rack sala server Toffano", - "label": "Temperatura zona rack" + "label": "Server rack zone temperature (ARCES, Toffano)" }, "5CCF7F15676D/humidity": { "observation": "arces-monitor:5CCF7F15676D-humidity", "unit": "qudt-unit-1-1:Percent", "location": "arces-monitor:Mars", "comment": "Umidità zona rack sala server Toffano", - "label": "Umidità zona rack" + "label": "Server rack zone humidity (ARCES, Toffano)" }, "5CCF7F1B599E/temperature": { "observation": "arces-monitor:5CCF7F1B599E-temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Mars", "comment": "Temperatura zona finestra sala server Toffano", - "label": "Temperatura zona finestra" + "label": "External temperature (ARCES, Toffano)" }, "5CCF7F1B599E/humidity": { "observation": "arces-monitor:5CCF7F1B599E-humidity", "unit": "qudt-unit-1-1:Percent", "location": "arces-monitor:Mars", "comment": "Umidità zona finestra sala server Toffano", - "label": "Umidità zona finestra" + "label": "External humidity (ARCES, Toffano)" }, "5CCF7F151DC9/temperature": { "observation": "arces-monitor:5CCF7F151DC9-temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Mars", "comment": "Temperatura esterna sala server Toffano", - "label": "Temperatura esterna" + "label": "Near window temperature (ARCES, Toffano)" }, "5CCF7F1B58AC/temperature": { "observation": "arces-monitor:5CCF7F1B58AC-temperature", "unit": "qudt-unit-1-1:DegreeCelsius", "location": "arces-monitor:Star", "comment": "Temperatura zona rack sala server Pepoli", - "label": "Temperatura zona rack" + "label": "Server rack zone temperature (ARCES, Pepoli)" }, "5CCF7F1B58AC/humidity": { "observation": "arces-monitor:5CCF7F1B58AC-humidity", "unit": "qudt-unit-1-1:Percent", "location": "arces-monitor:Star", "comment": "Umidità zona rack sala server Pepoli", - "label": "Umidità zona rack" + "label": "Server rack zone humidity (ARCES, Pepoli)" } } }, diff --git a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java similarity index 86% rename from tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java rename to tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java index b49d85bf..28f56806 100644 --- a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/chat/SEPAChatTest.java @@ -6,16 +6,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.junit.BeforeClass; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.pattern.JSAP; -import org.junit.Test; - -import static org.junit.Assert.*; public class SEPAChatTest { private static final Logger logger = LogManager.getLogger(); @@ -32,10 +28,9 @@ private static enum CLIENT_TYPE { private static CLIENT_TYPE type = CLIENT_TYPE.BASIC; - @BeforeClass public static void init() { try { - JSAP app = ConfigurationProvider.GetTestEnvConfiguration(); + JSAP app = new JSAP("chat.jsap"); BASE = app.getExtendedData().get("base").getAsInt(); N_CLIENTS = app.getExtendedData().get("clients").getAsInt(); @@ -54,7 +49,6 @@ public static void init() { } } - @Test public void chatTest() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException, IOException { deleteAllClients(); registerClients(); @@ -83,23 +77,19 @@ public void chatTest() throws SEPAProtocolException, SEPAPropertiesException, SE for (Thread th : clients) th.join(60000); - assertTrue("Chat tested",true); } - @Test + public void deleteAllClients() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { DeleteAll client = new DeleteAll(); client.clean(); try { client.close(); } catch (IOException e) { - assertFalse(e.getMessage(), true); } - - assertTrue("Delete all clients SUCCESS", true); } - @Test + public void registerClients() throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException { // Register chat BOTS UserRegistration registration = new UserRegistration(); @@ -109,9 +99,6 @@ public void registerClients() throws SEPAProtocolException, SEPAPropertiesExcept try { registration.close(); } catch (IOException e) { - assertFalse(e.getMessage(), true); } - - assertTrue("Clients registration SUCCESS", true); } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java index 1858b57c..d8b075c7 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTAdapter.java @@ -102,9 +102,9 @@ public void messageArrived(String topic, MqttMessage value) throws Exception { } public boolean start() throws SEPASecurityException { - /* + /* SWAMP: mosquitto_sub -h eu.thethings.network -p 1883 -u swamp -P ttn-account-v2.ES-s-MdMIHv8Z8HI5BR0FHzRjLD0WEmySE7cYM-Kepg -d -t 'swamp/devices/moisture1/up' * test.mosquitto.org 1883 - * giove.arces.unibo.it 52877 + * ARCES: giove.arces.unibo.it 52877 * * */ // MQTT @@ -130,6 +130,16 @@ public boolean start() throws SEPASecurityException { } else { serverURI = "tcp://" + url + ":" + String.format("%d", port); } + + String userName = null; + if (mqtt.has("username")) { + userName = mqtt.get("username").getAsString(); + } + + String password = null; + if (mqtt.has("password")) { + password = mqtt.get("password").getAsString(); + } // Create client logger.info("Creating MQTT client..."); @@ -143,8 +153,8 @@ public boolean start() throws SEPASecurityException { return false; } - // Connect - logger.info("Connecting..."); + // Options + logger.info("Setting options"); MqttConnectOptions options = new MqttConnectOptions(); if (sslEnabled) { logger.info("Set SSL security"); @@ -152,6 +162,17 @@ public boolean start() throws SEPASecurityException { SEPASecurityManager sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017"); options.setSocketFactory(sm.getSSLContext().getSocketFactory()); } + if (userName != null) { + logger.info("Set username: "+userName); + options.setUserName(userName); + } + if (password != null) { + logger.info("Set password: "+password); + options.setPassword(password.toCharArray()); + } + + // Connect + logger.info("Connecting..."); try { mqttClient.connect(options); } catch (MqttException e) { @@ -168,7 +189,7 @@ public boolean start() throws SEPASecurityException { return false; } - String printTopics = "Topic filter "; + String printTopics = " Topics: "; for (String s : topicsFilter) { printTopics += s + " "; } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java index ae26faff..e978af55 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTMapper.java @@ -5,6 +5,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.eclipse.paho.client.mqttv3.MqttException; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -20,6 +21,17 @@ public class MQTTMapper extends Producer { private static final Logger logger = LogManager.getLogger(); + public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException, MqttException { + if (args.length != 1) { + logger.error("Please provide the jsap file as argument"); + System.exit(-1); + } + + MQTTMapper client = new MQTTMapper(args[0]); + client.init(); + client.close(); + } + public MQTTMapper(String jsap) throws SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, IOException { super(new JSAP(jsap), "ADD_OBSERVATION"); } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java index 62bcb846..b46455cd 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/MQTTSmartifier.java @@ -43,6 +43,28 @@ public class MQTTSmartifier extends Aggregator implements MqttCallback { // Topics mapping private HashMap topic2observation = new HashMap(); + public static void main(String[] args) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException, MqttException { + if (args.length != 1) { + logger.error("Please provide the jsap file as argument"); + System.exit(-1); + } + + MQTTSmartifier smartifier = new MQTTSmartifier(args[0]); + smartifier.start(); + + logger.info("Press any key to exit..."); + try { + System.in.read(); + } catch (IOException e) { + logger.warn(e.getMessage()); + } + + logger.info("Stop MQTT smartifier"); + + smartifier.stop(); + smartifier.close(); + } + public MQTTSmartifier(String jsap) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(new JSAP(jsap), "OBSERVATIONS_TOPICS", "UPDATE_OBSERVATION_VALUE"); } @@ -54,15 +76,18 @@ public void onAddedResults(BindingsResults results) { } } - private void updateObservationValue(String observation, String value) throws SEPASecurityException, IOException, SEPAPropertiesException { + private void updateObservationValue(String observation, String value) + throws SEPASecurityException, IOException, SEPAPropertiesException { if (value != null) { - if (value.equals("NaN")) return; - - RDFTermLiteral literal = new RDFTermLiteral(value,appProfile.getUpdateBindings("UPDATE_OBSERVATION_VALUE").getDatatype("value")); - - setUpdateBindingValue("observation",new RDFTermURI(observation)); - setUpdateBindingValue("value",literal); - + if (value.equals("NaN")) + return; + + RDFTermLiteral literal = new RDFTermLiteral(value, + appProfile.getUpdateBindings("UPDATE_OBSERVATION_VALUE").getDatatype("value")); + + setUpdateBindingValue("observation", new RDFTermURI(observation)); + setUpdateBindingValue("value", literal); + update(); } } @@ -110,17 +135,18 @@ public void deliveryComplete(IMqttDeliveryToken arg0) { public void messageArrived(String topic, MqttMessage value) throws Exception { byte[] payload = value.getPayload(); String converted = ""; - for (int i =0; i < payload.length; i++) { - if (payload[i] == 0) break; - converted += String.format("%c",payload[i]); + for (int i = 0; i < payload.length; i++) { + if (payload[i] == 0) + break; + converted += String.format("%c", payload[i]); } - - mqttMessage(topic,converted); + + mqttMessage(topic, converted); } private void mqttMessage(String topic, String value) throws Exception { // String topicValue = value.toString(); - logger.debug(topic + " " + value); + logger.info(topic + " " + value); if (topic2observation.containsKey(topic)) { updateObservationValue(topic2observation.get(topic), value); @@ -174,7 +200,8 @@ else if (appProfile.getExtendedData().get("jsonTopics").getAsJsonObject().get(to } } - public void start() throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException, MqttException { + public void start() + throws SEPASecurityException, IOException, SEPAPropertiesException, SEPAProtocolException, MqttException { if (getApplicationProfile().getExtendedData().get("simulate").getAsBoolean()) simulator(); else { @@ -208,18 +235,28 @@ public void start() throws SEPASecurityException, IOException, SEPAPropertiesExc String clientID = MqttClient.generateClientId(); logger.info("Client ID: " + clientID); logger.info("Server URI: " + serverURI); - + mqttClient = new MqttClient(serverURI, clientID); - - // Connect - logger.info("Connecting..."); + + // Options MqttConnectOptions options = new MqttConnectOptions(); if (sslEnabled) { logger.info("Set SSL security"); - + SEPASecurityManager sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017"); options.setSocketFactory(sm.getSSLContext().getSocketFactory()); } + + if (mqtt.has("username")) { + options.setUserName(mqtt.get("username").getAsString()); + } + + if (mqtt.has("password")) { + options.setPassword(mqtt.get("password").getAsString().toCharArray()); + } + + // Connect + logger.info("Connecting..."); try { mqttClient.connect(options); } catch (MqttException e) { @@ -229,13 +266,14 @@ public void start() throws SEPASecurityException, IOException, SEPAPropertiesExc // Subscribe mqttClient.setCallback(this); logger.info("Subscribing..."); - + mqttClient.subscribe(topicsFilter); - - for(String topic:topicsFilter) logger.info("MQTT client " + clientID + " subscribed to " + serverURI + " Topic filter " + topic); + + for (String topic : topicsFilter) + logger.info("MQTT client " + clientID + " subscribed to " + serverURI + " Topic filter " + topic); // MQTT: end } - + // Subscribe to observation-topic mapping subscribe(5000); } @@ -244,20 +282,20 @@ public void simulator() { new Thread() { public void run() { JsonObject topics = getApplicationProfile().getExtendedData().get("simulation").getAsJsonObject(); - - while(true) { - for(Entry observation : topics.entrySet()) { + + while (true) { + for (Entry observation : topics.entrySet()) { String topic = observation.getKey(); int min = observation.getValue().getAsJsonArray().get(0).getAsInt(); int max = observation.getValue().getAsJsonArray().get(1).getAsInt(); - String value = String.format("%.2f", min + (Math.random() * (max-min))); - + String value = String.format("%.2f", min + (Math.random() * (max - min))); + try { mqttMessage(topic, value); } catch (Exception e) { logger.error(e.getMessage()); } - + try { Thread.sleep(1000); } catch (InterruptedException e) { @@ -267,65 +305,68 @@ public void run() { } } }.start(); - -// new Thread() { -// public void run() { -// // 6LowPan -// // e.g. pepoli:6lowpan:network = | ID: NODO1 | Temperature: 24.60 | Humidity: -// // 35.40 | Pressure: 1016.46 -// String pattern6LowPan = " | ID: %s | Temperature: %.2f | Humidity: %.2f | Pressure: %.2f"; -// String[] nodes6LowPan = { "NODO1", "NODO2", "NODO3" }; -// String topicLowPan = "pepoli:6lowpan:network"; -// -// // LoRa -// // e.g. {"moistureValue":3247, "nodeId":"device3", -// // "timestamp":"2017-11-15T10:00:02.123028089Z"} -// String patternLoRa = "{\"moistureValue\":%.2f, \"nodeId\":\"%s\", \"timestamp\":\"2017-11-15T10:00:02.123028089Z\"}"; -// String[] nodesLoRa = { "device1", "device2" }; -// String topicLoRa = "ground/lora/moisture"; -// -// while (true) { -// -// for (String node : nodes6LowPan) { -// for (int j = 0; j < 100; j++) { -// String value = String.format(pattern6LowPan, node, (float) j, (float)(100 - j), -// (float)(10 * j)); -// try { -// mqttMessage(topicLowPan, value); -// } catch (Exception e) { -// logger.error(e.getMessage()); -// } -// } -// } -// -// for (String device : nodesLoRa) { -// for (int j = 0; j < 100; j++) { -// String value = String.format(patternLoRa, (float) j, device); -// try { -// mqttMessage(topicLoRa, value); -// } catch (Exception e) { -// logger.error(e.getMessage()); -// } -// } -// } -// -// for (int i = 0; i < 35; i = i + 5) { -// for (String topic : topic2observation.keySet()) { -// if (topic.startsWith(topicLowPan.replace(":", "/"))) -// continue; -// if (topic.startsWith(topicLoRa.replace(":", "/"))) -// continue; -// -// try { -// mqttMessage(topic, String.format("%d", 10 + i)); -// } catch (Exception e) { -// logger.error(e.getMessage()); -// } -// } -// } -// } -// } -// }.start(); + + // new Thread() { + // public void run() { + // // 6LowPan + // // e.g. pepoli:6lowpan:network = | ID: NODO1 | Temperature: 24.60 | Humidity: + // // 35.40 | Pressure: 1016.46 + // String pattern6LowPan = " | ID: %s | Temperature: %.2f | Humidity: %.2f | + // Pressure: %.2f"; + // String[] nodes6LowPan = { "NODO1", "NODO2", "NODO3" }; + // String topicLowPan = "pepoli:6lowpan:network"; + // + // // LoRa + // // e.g. {"moistureValue":3247, "nodeId":"device3", + // // "timestamp":"2017-11-15T10:00:02.123028089Z"} + // String patternLoRa = "{\"moistureValue\":%.2f, \"nodeId\":\"%s\", + // \"timestamp\":\"2017-11-15T10:00:02.123028089Z\"}"; + // String[] nodesLoRa = { "device1", "device2" }; + // String topicLoRa = "ground/lora/moisture"; + // + // while (true) { + // + // for (String node : nodes6LowPan) { + // for (int j = 0; j < 100; j++) { + // String value = String.format(pattern6LowPan, node, (float) j, (float)(100 - + // j), + // (float)(10 * j)); + // try { + // mqttMessage(topicLowPan, value); + // } catch (Exception e) { + // logger.error(e.getMessage()); + // } + // } + // } + // + // for (String device : nodesLoRa) { + // for (int j = 0; j < 100; j++) { + // String value = String.format(patternLoRa, (float) j, device); + // try { + // mqttMessage(topicLoRa, value); + // } catch (Exception e) { + // logger.error(e.getMessage()); + // } + // } + // } + // + // for (int i = 0; i < 35; i = i + 5) { + // for (String topic : topic2observation.keySet()) { + // if (topic.startsWith(topicLowPan.replace(":", "/"))) + // continue; + // if (topic.startsWith(topicLoRa.replace(":", "/"))) + // continue; + // + // try { + // mqttMessage(topic, String.format("%d", 10 + i)); + // } catch (Exception e) { + // logger.error(e.getMessage()); + // } + // } + // } + // } + // } + // }.start(); } public void stop() { @@ -371,12 +412,12 @@ public void onError(ErrorResponse errorResponse) { @Override public void onSubscribe(String spuid, String alias) { // TODO Auto-generated method stub - + } @Override public void onUnsubscribe(String spuid) { // TODO Auto-generated method stub - + } } diff --git a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java index 6e29e3d6..c055a285 100644 --- a/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java +++ b/tools/src/main/java/it/unibo/arces/wot/sepa/apps/mqtt/ObservationLogger.java @@ -2,6 +2,10 @@ import java.io.IOException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.paho.client.mqttv3.MqttException; + import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -15,7 +19,29 @@ import it.unibo.arces.wot.sepa.pattern.JSAP; public class ObservationLogger extends Aggregator { - + private static final Logger logger = LogManager.getLogger(); + + public static void main(String[] args) + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, IOException, MqttException { + if (args.length != 1) { + logger.error("Please provide the jsap file as argument"); + System.exit(-1); + } + + // Logger + ObservationLogger analytics = new ObservationLogger(args[0]); + analytics.subscribe(5000); + + logger.info("Press any key to exit..."); + try { + System.in.read(); + } catch (IOException e) { + logger.warn(e.getMessage()); + } + + analytics.close(); + } + public ObservationLogger(String jsap) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { super(new JSAP(jsap), "OBSERVATIONS", "LOG_QUANTITY"); diff --git a/tools/src/main/resources/log4j2.xml b/tools/src/main/resources/log4j2.xml index 5bf8b582..c3506f6c 100644 --- a/tools/src/main/resources/log4j2.xml +++ b/tools/src/main/resources/log4j2.xml @@ -12,7 +12,7 @@ DEBUG 500 TRACE 600 ALL Integer.MAX_VALUE --> - + diff --git a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java b/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java deleted file mode 100644 index a9eb077b..00000000 --- a/tools/src/test/java/it/unibo/arces/wot/sepa/apps/chat/ConfigurationProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.unibo.arces.wot.sepa.apps.chat; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import java.net.URL; - -public class ConfigurationProvider { - - public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - JSAP result; - final String configuaration = System.getProperty("testConfiguration"); - if( configuaration != null){ - result = new JSAP(configuaration); - }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("chat.jsap"); - result = new JSAP(config.getPath()); - } - return result; - } -} From cfa1a34901e9334cc7ad25e578d3cf0a44c3d7d8 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 7 Sep 2018 09:02:23 +0200 Subject: [PATCH 37/76] Clean tools resources --- tools/endpoint.jpar | 1 - tools/src/main/resources/arces-demo-ttn.jsap | 667 ++++++++++++++++++ .../{ => src/main/resources}/arces-demo.jsap | 0 .../{ => src/main/resources}/mqtt-topics.jsap | 0 tools/{ => src/main/resources}/sepa.jks | Bin .../main/resources}/sepatest-secure.jsap | 0 tools/{ => src/main/resources}/sepatest.jsap | 0 .../{ => src/main/resources}/swamp-demo.jsap | 0 8 files changed, 667 insertions(+), 1 deletion(-) delete mode 100644 tools/endpoint.jpar create mode 100644 tools/src/main/resources/arces-demo-ttn.jsap rename tools/{ => src/main/resources}/arces-demo.jsap (100%) rename tools/{ => src/main/resources}/mqtt-topics.jsap (100%) rename tools/{ => src/main/resources}/sepa.jks (100%) rename tools/{ => src/main/resources}/sepatest-secure.jsap (100%) rename tools/{ => src/main/resources}/sepatest.jsap (100%) rename tools/{ => src/main/resources}/swamp-demo.jsap (100%) diff --git a/tools/endpoint.jpar b/tools/endpoint.jpar deleted file mode 100644 index 08e92a9f..00000000 --- a/tools/endpoint.jpar +++ /dev/null @@ -1 +0,0 @@ -{"ambiental":{"/ffa574972ab9/applink/107/001BC50C700009CD":{"observation":"arces-monitor:001BC50C700009CD-DASH7-Temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Star","comment":"Temperature sensor used to show the need of a new air conditioning system in SEHM lab","label":"Temperature SEHM lab"},"/ffa574972ab9/applink/107/001BC50C700009BB":{"observation":"arces-monitor:001BC50C700009BB-DASH7-Temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Star","comment":"emperature sensor used to show the need of a new air conditioning system in ST office","label":"Temperature ST office"},"5CCF7F15676D/temperature":{"observation":"arces-monitor:5CCF7F15676D-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura zona rack sala server Toffano","label":"Temperatura zona rack"},"5CCF7F15676D/humidity":{"observation":"arces-monitor:5CCF7F15676D-humidity","unit":"qudt-unit-1-1:Percent","location":"arces-monitor:Mars","comment":"Umidità zona rack sala server Toffano","label":"Umidità zona rack"},"5CCF7F1B599E/temperature":{"observation":"arces-monitor:5CCF7F1B599E-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura zona finestra sala server Toffano","label":"Temperatura zona finestra"},"5CCF7F1B599E/humidity":{"observation":"arces-monitor:5CCF7F1B599E-humidity","unit":"qudt-unit-1-1:Percent","location":"arces-monitor:Mars","comment":"Umidità zona finestra sala server Toffano","label":"Umidità zona finestra"},"5CCF7F151DC9/temperature":{"observation":"arces-monitor:5CCF7F151DC9-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura esterna sala server Toffano","label":"Temperatura esterna"},"5CCF7F1B58AC/temperature":{"observation":"arces-monitor:5CCF7F1B58AC-temperature","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Star","comment":"Temperatura zona rack sala server Pepoli","label":"Temperatura zona rack"},"5CCF7F1B58AC/humidity":{"observation":"arces-monitor:5CCF7F1B58AC-humidity","unit":"qudt-unit-1-1:Percent","location":"arces-monitor:Star","comment":"Umidità zona rack sala server Pepoli","label":"Umidità zona rack"}},"server":{"samba":{"arces/servers/mars/marsamba/hd/sda/temperature":{"observation":"arces-monitor:ServerSambaHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server SAMBA HDD SDA","label":"Temperatura Server SAMBA HDD SDA"},"arces/servers/mars/marsamba/hd/sdb/temperature":{"observation":"arces-monitor:ServerSambaHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server SAMBA HDD SDB","label":"Temperatura Server SAMBA HDD SDB"}},"giove":{"arces/servers/mars/giove/hd/sda/temperature":{"observation":"arces-monitor:ServerGioveHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDA","label":"Temperatura Server GIOVE HDD SDA"},"arces/servers/mars/giove/hd/sdb/temperature":{"observation":"arces-monitor:ServerGioveHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDB","label":"Temperatura Server GIOVE HDD SDB"},"arces/servers/mars/giove/hd/sdc/temperature":{"observation":"arces-monitor:ServerGioveHDDSdc","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDC","label":"Temperatura Server GIOVE HDD SDC"},"arces/servers/mars/giove/hd/sdd/temperature":{"observation":"arces-monitor:ServerGioveHDDSdd","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDD","label":"Temperatura Server GIOVE HDD SDD"},"arces/servers/mars/giove/hd/sdf/temperature":{"observation":"arces-monitor:ServerGioveHDDSdf","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDF","label":"Temperatura Server GIOVE HDD SDF"},"arces/servers/mars/giove/hd/sdg/temperature":{"observation":"arces-monitor:ServerGioveHDDSdg","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDG","label":"Temperatura Server GIOVE HDD SDG"},"arces/servers/mars/giove/hd/sdh/temperature":{"observation":"arces-monitor:ServerGioveHDDSdh","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE HDD SDH","label":"Temperatura Server GIOVE HDD SDH"},"arces/servers/mars/giove/cpu/core-6/temperature":{"observation":"arces-monitor:ServerGiove6","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 6","label":"Temperatura Server GIOVE Core 6"},"arces/servers/mars/giove/cpu/core-5/temperature":{"observation":"arces-monitor:ServerGiove5","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 5","label":"Temperatura Server GIOVE Core 5"},"arces/servers/mars/giove/cpu/core-4/temperature":{"observation":"arces-monitor:ServerGiove4","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 4","label":"Temperatura Server GIOVE Core 4"},"arces/servers/mars/giove/cpu/core-3/temperature":{"observation":"arces-monitor:ServerGiove3","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 3","label":"Temperatura Server GIOVE Core 3"},"arces/servers/mars/giove/cpu/core-2/temperature":{"observation":"arces-monitor:ServerGiove2","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 2","label":"Temperatura Server GIOVE Core 2"},"arces/servers/mars/giove/cpu/core-1/temperature":{"observation":"arces-monitor:ServerGiove1","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server GIOVE Core 1","label":"Temperatura Server GIOVE Core 1"}},"mml":{"arces/servers/mars/mml/hd/sda/temperature":{"observation":"arces-monitor:ServerMmlHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML HDD SDA","label":"Temperatura Server MML HDD SDA"},"arces/servers/mars/mml/hd/sdb/temperature":{"observation":"arces-monitor:ServerMmlHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML HDD SDB","label":"Temperatura Server MML HDD SDB"},"arces/servers/mars/mml/cpu/core-6/temperature":{"observation":"arces-monitor:ServerMml6","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 6","label":"Temperatura Server MML Core 6"},"arces/servers/mars/mml/cpu/core-5/temperature":{"observation":"arces-monitor:ServerMml5","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 5","label":"Temperatura Server MML Core 5"},"arces/servers/mars/mml/cpu/core-4/temperature":{"observation":"arces-monitor:ServerMml4","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 4","label":"Temperatura Server MML Core 4"},"arces/servers/mars/mml/cpu/core-3/temperature":{"observation":"arces-monitor:ServerMml3","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 3","label":"Temperatura Server MML Core 3"},"arces/servers/mars/mml/cpu/core-2/temperature":{"observation":"arces-monitor:ServerMml2","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 2","label":"Temperatura Server MML Core 2"},"arces/servers/mars/mml/cpu/core-1/temperature":{"observation":"arces-monitor:ServerMml1","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Mars","comment":"Temperatura Server MML Core 1","label":"Temperatura Server MML Core 1"}},"ercole":{"arces/servers/ares/ercole/cpu/core-20/temperature":{"observation":"arces-monitor:ServerErcoleCore20","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 20","label":"Temperatura Server ERCOLE Core 20"},"arces/servers/ares/ercole/cpu/core-19/temperature":{"observation":"arces-monitor:ServerErcoleCore19","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 19","label":"Temperatura Server ERCOLE Core 19"},"arces/servers/ares/ercole/cpu/core-18/temperature":{"observation":"arces-monitor:ServerErcoleCore18","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 18","label":"Temperatura Server ERCOLE Core 18"},"arces/servers/ares/ercole/cpu/core-17/temperature":{"observation":"arces-monitor:ServerErcoleCore17","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 17","label":"Temperatura Server ERCOLE Core 17"},"arces/servers/ares/ercole/cpu/core-16/temperature":{"observation":"arces-monitor:ServerErcoleCore16","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 16","label":"Temperatura Server ERCOLE Core 16"},"arces/servers/ares/ercole/cpu/core-15/temperature":{"observation":"arces-monitor:ServerErcoleCore15","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 15","label":"Temperatura Server ERCOLE Core 15"},"arces/servers/ares/ercole/cpu/core-14/temperature":{"observation":"arces-monitor:ServerErcoleCore14","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 14","label":"Temperatura Server ERCOLE Core 14"},"arces/servers/ares/ercole/cpu/core-13/temperature":{"observation":"arces-monitor:ServerErcoleCore13","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 13","label":"Temperatura Server ERCOLE Core 13"},"arces/servers/ares/ercole/cpu/core-12/temperature":{"observation":"arces-monitor:ServerErcoleCore12","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 12","label":"Temperatura Server ERCOLE Core 12"},"arces/servers/ares/ercole/cpu/core-11/temperature":{"observation":"arces-monitor:ServerErcoleCore11","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 11","label":"Temperatura Server ERCOLE Core 11"},"arces/servers/ares/ercole/cpu/core-10/temperature":{"observation":"arces-monitor:ServerErcoleCore10","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 10","label":"Temperatura Server ERCOLE Core 10"},"arces/servers/ares/ercole/cpu/core-9/temperature":{"observation":"arces-monitor:ServerErcoleCore9","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 9","label":"Temperatura Server ERCOLE Core 9"},"arces/servers/ares/ercole/cpu/core-8/temperature":{"observation":"arces-monitor:ServerErcoleCore8","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 8","label":"Temperatura Server ERCOLE Core 8"},"arces/servers/ares/ercole/cpu/core-7/temperature":{"observation":"arces-monitor:ServerErcoleCore7","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 7","label":"Temperatura Server ERCOLE Core 7"},"arces/servers/ares/ercole/cpu/core-6/temperature":{"observation":"arces-monitor:ServerErcoleCore6","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 6","label":"Temperatura Server ERCOLE Core 6"},"arces/servers/ares/ercole/cpu/core-5/temperature":{"observation":"arces-monitor:ServerErcoleCore5","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 5","label":"Temperatura Server ERCOLE Core 5"},"arces/servers/ares/ercole/cpu/core-4/temperature":{"observation":"arces-monitor:ServerErcoleCore4","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 4","label":"Temperatura Server ERCOLE Core 4"},"arces/servers/ares/ercole/cpu/core-3/temperature":{"observation":"arces-monitor:ServerErcoleCore3","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 3","label":"Temperatura Server ERCOLE Core 3"},"arces/servers/ares/ercole/cpu/core-2/temperature":{"observation":"arces-monitor:ServerErcoleCore2","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 2","label":"Temperatura Server ERCOLE Core 2"},"arces/servers/ares/ercole/cpu/core-1/temperature":{"observation":"arces-monitor:ServerErcoleCore1","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE Core 1","label":"Temperatura Server ERCOLE Core 1"},"arces/servers/ares/ercole/hd/sda/temperature":{"observation":"arces-monitor:ServerErcoleHDDSda","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE HDD SDA","label":"Temperatura Server ERCOLE HDD SDA"},"arces/servers/ares/ercole/hd/sdb/temperature":{"observation":"arces-monitor:ServerErcoleHDDSdb","unit":"qudt-unit-1-1:DegreeCelsius","location":"arces-monitor:Ares","comment":"Temperatura Server ERCOLE HDD SDB","label":"Temperatura Server ERCOLE HDD SDB"}}},"host":"localhost","sparql11protocol":{"protocol":"http","port":9999,"query":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"},"update":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"ws","availableProtocols":{"ws":{"port":9443,"path":"/subscribe"},"wss":{}}}} \ No newline at end of file diff --git a/tools/src/main/resources/arces-demo-ttn.jsap b/tools/src/main/resources/arces-demo-ttn.jsap new file mode 100644 index 00000000..0ee788c7 --- /dev/null +++ b/tools/src/main/resources/arces-demo-ttn.jsap @@ -0,0 +1,667 @@ +{ + "host": "mml.arces.unibo.it", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", + "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", + "using-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt", + "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt" + }, + "namespaces": { + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "sosa": "http://www.w3.org/ns/sosa/", + "qudt-1-1": "http://qudt.org/1.1/schema/qudt#", + "qudt-unit-1-1": "http://qudt.org/1.1/vocab/unit#", + "arces-monitor": "http://wot.arces.unibo.it/monitor#", + "mqtt": "http://wot.arces.unibo.it/mqtt#", + "time": "http://www.w3.org/2006/time#", + "wgs84_pos": "http://www.w3.org/2003/01/geo/wgs84_pos#", + "gn": "http://www.geonames.org/ontology#" + }, + "extended": { + "simulate": false, + "simulation" : { + "italy/site1/soilmoisture" : [35,75], + "italy/site1/pressure" : [900,1100], + "italy/site1/temperature" : [15,40] + }, + "mqtt": { + "url": "eu.thethings.network", + "port": 1883, + "topics": [ + "swamp/devices/moisture1/up" + ], + "ssl": false, + "username" : "swamp", + "password" : "ttn-account-v2.ES-s-MdMIHv8Z8HI5BR0FHzRjLD0WEmySE7cYM-Kepg" + }, + "regexTopics": { + "pepoli:6lowpan:network": [ + ".[|].ID:.(\\w+).[|].(Temperature)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d+\n", + ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].(Humidity)[:].(?\\d+.\\d++).[|].\\w+[:].\\d+.\\d+\n", + ".[|].ID:.(\\w+).[|].\\w+[:].\\d+.\\d++.[|].\\w+[:].\\d+.\\d++.[|].(Pressure)[:].(?\\d+.\\d+)\n" + ] + }, + "jsonTopics": { + "ground/lora/moisture": { + "id": "nodeId", + "value": "moistureValue" + } + }, + "semantic-mappings": { + "/ffa574972ab9/applink/107/001BC50C700009CD": { + "observation": "arces-monitor:001BC50C700009CD-DASH7-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperature sensor used to show the need of a new air conditioning system in SEHM lab", + "label": "Temperature SEHM lab" + }, + "/ffa574972ab9/applink/107/001BC50C700009BB": { + "observation": "arces-monitor:001BC50C700009BB-DASH7-Temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "emperature sensor used to show the need of a new air conditioning system in ST office", + "label": "Temperature ST office" + }, + "arces/servers/ares/ercole/cpu/core-20/temperature": { + "observation": "arces-monitor:ServerErcoleCore20", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 20", + "label": "Temperatura Server ERCOLE Core 20" + }, + "arces/servers/ares/ercole/cpu/core-19/temperature": { + "observation": "arces-monitor:ServerErcoleCore19", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 19", + "label": "Temperatura Server ERCOLE Core 19" + }, + "arces/servers/ares/ercole/cpu/core-18/temperature": { + "observation": "arces-monitor:ServerErcoleCore18", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 18", + "label": "Temperatura Server ERCOLE Core 18" + }, + "arces/servers/ares/ercole/cpu/core-17/temperature": { + "observation": "arces-monitor:ServerErcoleCore17", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 17", + "label": "Temperatura Server ERCOLE Core 17" + }, + "arces/servers/ares/ercole/cpu/core-16/temperature": { + "observation": "arces-monitor:ServerErcoleCore16", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 16", + "label": "Temperatura Server ERCOLE Core 16" + }, + "arces/servers/ares/ercole/cpu/core-15/temperature": { + "observation": "arces-monitor:ServerErcoleCore15", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 15", + "label": "Temperatura Server ERCOLE Core 15" + }, + "arces/servers/ares/ercole/cpu/core-14/temperature": { + "observation": "arces-monitor:ServerErcoleCore14", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 14", + "label": "Temperatura Server ERCOLE Core 14" + }, + "arces/servers/ares/ercole/cpu/core-13/temperature": { + "observation": "arces-monitor:ServerErcoleCore13", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 13", + "label": "Temperatura Server ERCOLE Core 13" + }, + "arces/servers/ares/ercole/cpu/core-12/temperature": { + "observation": "arces-monitor:ServerErcoleCore12", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 12", + "label": "Temperatura Server ERCOLE Core 12" + }, + "arces/servers/ares/ercole/cpu/core-11/temperature": { + "observation": "arces-monitor:ServerErcoleCore11", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 11", + "label": "Temperatura Server ERCOLE Core 11" + }, + "arces/servers/ares/ercole/cpu/core-10/temperature": { + "observation": "arces-monitor:ServerErcoleCore10", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 10", + "label": "Temperatura Server ERCOLE Core 10" + }, + "arces/servers/ares/ercole/cpu/core-9/temperature": { + "observation": "arces-monitor:ServerErcoleCore9", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 9", + "label": "Temperatura Server ERCOLE Core 9" + }, + "arces/servers/ares/ercole/cpu/core-8/temperature": { + "observation": "arces-monitor:ServerErcoleCore8", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 8", + "label": "Temperatura Server ERCOLE Core 8" + }, + "arces/servers/ares/ercole/cpu/core-7/temperature": { + "observation": "arces-monitor:ServerErcoleCore7", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 7", + "label": "Temperatura Server ERCOLE Core 7" + }, + "arces/servers/ares/ercole/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerErcoleCore6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 6", + "label": "Temperatura Server ERCOLE Core 6" + }, + "arces/servers/ares/ercole/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerErcoleCore5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 5", + "label": "Temperatura Server ERCOLE Core 5" + }, + "arces/servers/ares/ercole/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerErcoleCore4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 4", + "label": "Temperatura Server ERCOLE Core 4" + }, + "arces/servers/ares/ercole/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerErcoleCore3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 3", + "label": "Temperatura Server ERCOLE Core 3" + }, + "arces/servers/ares/ercole/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerErcoleCore2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 2", + "label": "Temperatura Server ERCOLE Core 2" + }, + "arces/servers/ares/ercole/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerErcoleCore1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE Core 1", + "label": "Temperatura Server ERCOLE Core 1" + }, + "arces/servers/mars/mml/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerMml6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 6", + "label": "Temperatura Server MML Core 6" + }, + "arces/servers/mars/mml/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerMml5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 5", + "label": "Temperatura Server MML Core 5" + }, + "arces/servers/mars/mml/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerMml4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 4", + "label": "Temperatura Server MML Core 4" + }, + "arces/servers/mars/mml/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerMml3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 3", + "label": "Temperatura Server MML Core 3" + }, + "arces/servers/mars/mml/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerMml2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 2", + "label": "Temperatura Server MML Core 2" + }, + "arces/servers/mars/mml/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerMml1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML Core 1", + "label": "Temperatura Server MML Core 1" + }, + "arces/servers/mars/giove/cpu/core-6/temperature": { + "observation": "arces-monitor:ServerGiove6", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 6", + "label": "Temperatura Server GIOVE Core 6" + }, + "arces/servers/mars/giove/cpu/core-5/temperature": { + "observation": "arces-monitor:ServerGiove5", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 5", + "label": "Temperatura Server GIOVE Core 5" + }, + "arces/servers/mars/giove/cpu/core-4/temperature": { + "observation": "arces-monitor:ServerGiove4", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 4", + "label": "Temperatura Server GIOVE Core 4" + }, + "arces/servers/mars/giove/cpu/core-3/temperature": { + "observation": "arces-monitor:ServerGiove3", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 3", + "label": "Temperatura Server GIOVE Core 3" + }, + "arces/servers/mars/giove/cpu/core-2/temperature": { + "observation": "arces-monitor:ServerGiove2", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 2", + "label": "Temperatura Server GIOVE Core 2" + }, + "arces/servers/mars/giove/cpu/core-1/temperature": { + "observation": "arces-monitor:ServerGiove1", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE Core 1", + "label": "Temperatura Server GIOVE Core 1" + }, + "arces/servers/mars/mml/hd/sda/temperature": { + "observation": "arces-monitor:ServerMmlHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML HDD SDA", + "label": "Temperatura Server MML HDD SDA" + }, + "arces/servers/mars/mml/hd/sdb/temperature": { + "observation": "arces-monitor:ServerMmlHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server MML HDD SDB", + "label": "Temperatura Server MML HDD SDB" + }, + "arces/servers/mars/giove/hd/sda/temperature": { + "observation": "arces-monitor:ServerGioveHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDA", + "label": "Temperatura Server GIOVE HDD SDA" + }, + "arces/servers/mars/giove/hd/sdb/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDB", + "label": "Temperatura Server GIOVE HDD SDB" + }, + "arces/servers/mars/giove/hd/sdc/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdc", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDC", + "label": "Temperatura Server GIOVE HDD SDC" + }, + "arces/servers/mars/giove/hd/sdd/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdd", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDD", + "label": "Temperatura Server GIOVE HDD SDD" + }, + "arces/servers/mars/giove/hd/sdf/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdf", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDF", + "label": "Temperatura Server GIOVE HDD SDF" + }, + "arces/servers/mars/giove/hd/sdg/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdg", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDG", + "label": "Temperatura Server GIOVE HDD SDG" + }, + "arces/servers/mars/giove/hd/sdh/temperature": { + "observation": "arces-monitor:ServerGioveHDDSdh", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server GIOVE HDD SDH", + "label": "Temperatura Server GIOVE HDD SDH" + }, + "arces/servers/mars/marsamba/hd/sda/temperature": { + "observation": "arces-monitor:ServerSambaHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server SAMBA HDD SDA", + "label": "Temperatura Server SAMBA HDD SDA" + }, + "arces/servers/mars/marsamba/hd/sdb/temperature": { + "observation": "arces-monitor:ServerSambaHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura Server SAMBA HDD SDB", + "label": "Temperatura Server SAMBA HDD SDB" + }, + "arces/servers/ares/ercole/hd/sda/temperature": { + "observation": "arces-monitor:ServerErcoleHDDSda", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE HDD SDA", + "label": "Temperatura Server ERCOLE HDD SDA" + }, + "arces/servers/ares/ercole/hd/sdb/temperature": { + "observation": "arces-monitor:ServerErcoleHDDSdb", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Ares", + "comment": "Temperatura Server ERCOLE HDD SDB", + "label": "Temperatura Server ERCOLE HDD SDB" + }, + "5CCF7F15676D/temperature": { + "observation": "arces-monitor:5CCF7F15676D-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura zona rack sala server Toffano", + "label": "Temperatura zona rack" + }, + "5CCF7F15676D/humidity": { + "observation": "arces-monitor:5CCF7F15676D-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Mars", + "comment": "Umidità zona rack sala server Toffano", + "label": "Umidità zona rack" + }, + "5CCF7F1B599E/temperature": { + "observation": "arces-monitor:5CCF7F1B599E-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura zona finestra sala server Toffano", + "label": "Temperatura zona finestra" + }, + "5CCF7F1B599E/humidity": { + "observation": "arces-monitor:5CCF7F1B599E-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Mars", + "comment": "Umidità zona finestra sala server Toffano", + "label": "Umidità zona finestra" + }, + "5CCF7F151DC9/temperature": { + "observation": "arces-monitor:5CCF7F151DC9-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Mars", + "comment": "Temperatura esterna sala server Toffano", + "label": "Temperatura esterna" + }, + "5CCF7F1B58AC/temperature": { + "observation": "arces-monitor:5CCF7F1B58AC-temperature", + "unit": "qudt-unit-1-1:DegreeCelsius", + "location": "arces-monitor:Star", + "comment": "Temperatura zona rack sala server Pepoli", + "label": "Temperatura zona rack" + }, + "5CCF7F1B58AC/humidity": { + "observation": "arces-monitor:5CCF7F1B58AC-humidity", + "unit": "qudt-unit-1-1:Percent", + "location": "arces-monitor:Star", + "comment": "Umidità zona rack sala server Pepoli", + "label": "Umidità zona rack" + } + } + }, + "updates": { + "REMOVE_PLACE": { + "sparql": "DELETE WHERE {?place ?s ?p}", + "forcedBindings": { + "place": { + "type": "uri", + "value": "arces-monitor:Mars" + } + } + }, + "ADD_PLACE": { + "sparql": "INSERT DATA {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}", + "forcedBindings": { + "place": { + "type": "uri", + "value": "arces-monitor:Mars" + }, + "name": { + "type": "literal", + "value": "Mars" + }, + "lat": { + "type": "literal", + "value": "44.489664", + "datatype": "xsd:decimal" + }, + "long": { + "type": "literal", + "value": "11.357023", + "datatype": "xsd:decimal" + } + } + }, + "MQTT_MESSAGE": { + "sparql": "DELETE {?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} INSERT {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?timestamp} WHERE {OPTIONAL{?oldMessage rdf:type mqtt:Message ; mqtt:hasValue ?oldValue ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker ; mqtt:timestamp ?oldTimestamp} . BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/mqtt#Message-\",STRUUID())) AS ?message) . BIND(now() AS ?timestamp)}", + "forcedBindings": { + "value": { + "type": "literal", + "value": "mqttValueXYZ" + }, + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + }, + "broker": { + "type": "uri", + "value": "tcp://giove.arces.unibo.it:52887" + } + } + }, + "LOG_QUANTITY": { + "sparql": "INSERT {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value ; qudt-1-1:unit ?unit ; time:inXSDDateTimeStamp ?timestamp} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#Log-\",STRUUID())) AS ?log) . BIND(now() AS ?timestamp)}", + "graphs": { + "using-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log", + "using-named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log" + }, + "forcedBindings": { + "quantity": { + "type": "uri", + "value": "arces-monitor:QuantityValueXYZ" + }, + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + }, + "value": { + "type": "literal", + "value": "1234" + } + } + }, + "REMOVE_OBSERVATION": { + "sparql": "DELETE WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit . ?quantity qudt-1-1:numericValue ?value}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "UPDATE_OBSERVATION_LABEL": { + "sparql": "DELETE {?observation rdfs:label ?oldLabel} INSERT {?observation rdfs:label ?label} WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?oldLabel}", + "forcedBindings": { + "observation": { + "type": "xsd:string", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "ADD_OBSERVATION": { + "sparql": "INSERT {?observation rdf:type sosa:Observation ; rdfs:label ?label ; rdfs:comment ?comment ; sosa:hasFeatureOfInterest ?location ; arces-monitor:hasMqttTopic ?topic ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue 'NaN'} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/monitor#QuantityValue-\",STRUUID())) AS ?quantity)}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + }, + "comment": { + "type": "literal", + "value": "This is an observation" + }, + "label": { + "type": "literal", + "value": "The observation XYZ" + }, + "location": { + "type": "uri", + "value": "arces-monitor:Mars" + }, + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + }, + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + } + } + }, + "UPDATE_OBSERVATION_VALUE": { + "sparql": "DELETE {?quantity qudt-1-1:numericValue ?oldValue} INSERT {?quantity qudt-1-1:numericValue ?value} WHERE {?observation rdf:type sosa:Observation ; sosa:hasResult ?quantity . OPTIONAL {?quantity qudt-1-1:numericValue ?oldValue}}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + }, + "value": { + "type": "literal", + "datatype": "xsd:decimal", + "value": "12345.67890" + } + } + } + }, + "queries": { + "LOG_TRIPLES_NUMBER" :{ + "sparql" : "SELECT (COUNT(?x) AS ?n) WHERE {?x ?y ?z}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log", + "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log" + } + }, + "LOG_QUANTITY": { + "sparql": "SELECT * WHERE {?log arces-monitor:refersTo ?quantity ; qudt-1-1:numericValue ?value; time:inXSDDateTimeStamp ?timestamp}", + "graphs": { + "default-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log", + "named-graph-uri": "http://wot.arces.unibo.it/monitor/mqtt/log" + } + }, + "PLACES": { + "sparql": "SELECT * WHERE {?place rdf:type gn:Feature; gn:name ?name ; wgs84_pos:lat ?lat ; wgs84_pos:long ?long}" + }, + "OBSERVATIONS_TOPICS": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; arces-monitor:hasMqttTopic ?topic}" + }, + "OBSERVATIONS": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit . OPTIONAL {?quantity qudt-1-1:numericValue ?value}}" + }, + "OBSERVATIONS_BY_LOCATION": { + "sparql": "SELECT * WHERE { ?observation sosa:hasFeatureOfInterest ?location ; rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "location": { + "type": "uri", + "value": "arces-monitor:Mars" + } + } + }, + "OBSERVATIONS_BY_UNIT": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "unit": { + "type": "uri", + "value": "qudt-unit-1-1:DegreeCelsius" + } + } + }, + "ALL_VALUES": { + "sparql": "SELECT * WHERE {?observation rdf:type sosa:Observation ; rdfs:label ?label ; sosa:hasFeatureOfInterest ?location ; sosa:hasResult ?quantity . ?quantity rdf:type qudt-1-1:QuantityValue ; qudt-1-1:unit ?unit ; qudt-1-1:numericValue ?value}", + "forcedBindings": { + "observation": { + "type": "uri", + "value": "arces-monitor:ObservationXYZ" + } + } + }, + "MQTT_TOPICS_COUNT": { + "sparql": "SELECT (COUNT(DISTINCT ?topic) AS ?topics) WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic}" + }, + "MQTT_TOPICS": { + "sparql": "SELECT DISTINCT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" + }, + "MQTT_TOPIC_VALUE": { + "sparql": "SELECT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasTopic ?topic; mqtt:hasValue ?value}", + "forcedBindings": { + "topic": { + "type": "literal", + "value": "mqttTopicXYZ" + } + } + }, + "MQTT_MESSAGES": { + "sparql": "SELECT * WHERE {?message rdf:type mqtt:Message ; mqtt:hasValue ?value ; mqtt:hasTopic ?topic ; mqtt:hasBroker ?broker}" + } + } +} \ No newline at end of file diff --git a/tools/arces-demo.jsap b/tools/src/main/resources/arces-demo.jsap similarity index 100% rename from tools/arces-demo.jsap rename to tools/src/main/resources/arces-demo.jsap diff --git a/tools/mqtt-topics.jsap b/tools/src/main/resources/mqtt-topics.jsap similarity index 100% rename from tools/mqtt-topics.jsap rename to tools/src/main/resources/mqtt-topics.jsap diff --git a/tools/sepa.jks b/tools/src/main/resources/sepa.jks similarity index 100% rename from tools/sepa.jks rename to tools/src/main/resources/sepa.jks diff --git a/tools/sepatest-secure.jsap b/tools/src/main/resources/sepatest-secure.jsap similarity index 100% rename from tools/sepatest-secure.jsap rename to tools/src/main/resources/sepatest-secure.jsap diff --git a/tools/sepatest.jsap b/tools/src/main/resources/sepatest.jsap similarity index 100% rename from tools/sepatest.jsap rename to tools/src/main/resources/sepatest.jsap diff --git a/tools/swamp-demo.jsap b/tools/src/main/resources/swamp-demo.jsap similarity index 100% rename from tools/swamp-demo.jsap rename to tools/src/main/resources/swamp-demo.jsap From 33bcbc9f406bc2f6d08fcc5bbe3044996ec385f7 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 7 Sep 2018 09:04:09 +0200 Subject: [PATCH 38/76] Cleaned tools test --- tools/src/test/resources/chat.jsap | 189 ----------------------------- 1 file changed, 189 deletions(-) delete mode 100644 tools/src/test/resources/chat.jsap diff --git a/tools/src/test/resources/chat.jsap b/tools/src/test/resources/chat.jsap deleted file mode 100644 index 816c7c68..00000000 --- a/tools/src/test/resources/chat.jsap +++ /dev/null @@ -1,189 +0,0 @@ -{ - "host": "localhost", - "oauth": { - "enable" : false, - "register": "https://localhost:8443/oauth/register", - "tokenRequest": "https://localhost:8443/oauth/token" - }, - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/secure/subscribe" - } - } - }, - "extended": { - "type": "basic", - "base": 0, - "clients": 10, - "messages": 1 - }, - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/chat", - "named-graph-uri": "http://wot.arces.unibo.it/chat", - "using-graph-uri": "http://wot.arces.unibo.it/chat", - "using-named-graph-uri": "http://wot.arces.unibo.it/chat" - }, - "namespaces": { - "schema": "http://schema.org/", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "chat": "http://wot.arces.unibo.it/chat#" - }, - "updates": { - "SEND": { - "sparql": "INSERT {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?time} WHERE {?sender rdf:type schema:Person . ?receiver rdf:type schema:Person BIND(STR(now()) AS ?time) BIND(IRI(CONCAT(\"http://schema.org/Message-\",STRUUID())) AS ?message)}", - "forcedBindings": { - "text": { - "type": "literal", - "value": "Ciao!" - }, - "sender": { - "type": "uri", - "value": "chat:IamASender" - }, - "receiver": { - "type": "uri", - "value": "chat:IamAReceiver" - } - } - }, - "SET_RECEIVED": { - "sparql": "INSERT {?message schema:dateReceived ?time} WHERE {?message rdf:type schema:Message BIND(STR(now()) AS ?time)}", - "forcedBindings": { - "message": { - "type": "uri", - "value": "chat:ThisIsAMessage" - } - } - }, - "REMOVE": { - "sparql": "DELETE {?message ?p ?o} WHERE {?message rdf:type schema:Message ; ?p ?o}", - "forcedBindings": { - "message": { - "type": "uri", - "value": "chat:ThisIsAMessage" - } - } - }, - "STORE_SENT": { - "sparql": "INSERT DATA {?message schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?dateSent}", - "forcedBindings": { - "dateSent": { - "type": "literal", - "value": "2018-06-28T00:00:00", - "datatype" : "xsd:dateTime" - }, - "message": { - "type": "uri", - "value": "chat:ThisIsAMessage" - }, - "text": { - "type": "literal", - "value": "A message to be stored" - }, - "sender": { - "type": "uri", - "value": "chat:IAmASender" - }, - "receiver": { - "type": "uri", - "value": "chat:IAmAReceiver" - } - }, - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/chat/log", - "using-named-graph-uri": "http://wot.arces.unibo.it/chat/log" - } - }, - "STORE_RECEIVED": { - "sparql": "INSERT DATA {?message schema:dateReceived ?dateReceived}", - "forcedBindings": { - "dateReceived": { - "type": "literal", - "value": "2018-06-28T00:00:00", - "datatype" : "xsd:dateTime" - }, - "message": { - "type": "uri", - "value": "chat:ThisIsAMessage" - } - }, - "graphs": { - "using-graph-uri": "http://wot.arces.unibo.it/chat/log", - "using-named-graph-uri": "http://wot.arces.unibo.it/chat/log" - } - }, - "REGISTER_USER": { - "sparql": "DELETE {?x rdf:type schema:Person . ?x schema:name ?userName} WHERE {?x rdf:type schema:Person . ?x schema:name ?userName} ; INSERT {?id rdf:type schema:Person ; schema:name ?userName} WHERE {BIND(IRI(CONCAT(\"http://schema.org/Person-\",STRUUID())) AS ?id)}", - "forcedBindings": { - "userName": { - "type": "literal", - "value": "My user name" - } - } - }, - "DELETE_ALL": { - "sparql": "delete {?s ?p ?o} where {?s ?p ?o}" - } - }, - "queries": { - "SENT": { - "sparql": "SELECT ?message ?sender ?name ?text ?time WHERE {?message rdf:type schema:Message ; schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver ; schema:dateSent ?time . ?sender rdf:type schema:Person ; schema:name ?name . ?receiver rdf:type schema:Person} ORDER BY ?time", - "forcedBindings": { - "receiver": { - "type": "uri", - "value": "chat:IAmAReceiver" - } - } - }, - "RECEIVED": { - "sparql": "SELECT ?message ?time WHERE {?message schema:sender ?sender ; schema:dateReceived ?time ; rdf:type schema:Message}", - "forcedBindings": { - "sender": { - "type": "uri", - "value": "chat:IAmASender" - } - } - }, - "LOG_SENT": { - "sparql": "SELECT ?message ?sender ?receiver ?text ?dateSent WHERE {?message schema:text ?text ; schema:sender ?sender ; schema:toRecipient ?receiver; schema:dateSent ?dateSent}", - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/chat/log", - "named-graph-uri": "http://wot.arces.unibo.it/chat/log" - } - }, - "LOG_RECEIVED": { - "sparql": "SELECT ?message ?dateReceived WHERE {?message schema:dateReceived ?dateReceived}", - "graphs": { - "default-graph-uri": "http://wot.arces.unibo.it/chat/log", - "named-graph-uri": "http://wot.arces.unibo.it/chat/log" - } - }, - "USERS": { - "sparql": "SELECT ?user ?userName WHERE {?user rdf:type schema:Person ; schema:name ?userName}" - }, - "QUERY_ALL": { - "sparql": "select * where {?s ?p ?o}" - } - } -} From d21a9f792e3a2d3078f520915fe5fb809f4759e4 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 7 Sep 2018 09:08:32 +0200 Subject: [PATCH 39/76] Client API resources cleaned --- client-api/endpoint.jpar | 1 - client-api/src/main/resources/endpoint.jpar | 29 +++++++++++++++++++ client-api/{ => src/main/resources}/sepa.jks | Bin client-api/src/test/resources/sepa.jks | Bin 0 -> 2317 bytes 4 files changed, 29 insertions(+), 1 deletion(-) delete mode 100644 client-api/endpoint.jpar create mode 100644 client-api/src/main/resources/endpoint.jpar rename client-api/{ => src/main/resources}/sepa.jks (100%) create mode 100644 client-api/src/test/resources/sepa.jks diff --git a/client-api/endpoint.jpar b/client-api/endpoint.jpar deleted file mode 100644 index 5ea97c64..00000000 --- a/client-api/endpoint.jpar +++ /dev/null @@ -1 +0,0 @@ -{"host":"localhost","sparql11protocol":{"protocol":"http","port":9999,"query":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"},"update":{"path":"/blazegraph/namespace/kb/sparql","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"ws","availableProtocols":{"ws":{"port":9443,"path":"/subscribe"},"wss":{}}}} \ No newline at end of file diff --git a/client-api/src/main/resources/endpoint.jpar b/client-api/src/main/resources/endpoint.jpar new file mode 100644 index 00000000..8214e8db --- /dev/null +++ b/client-api/src/main/resources/endpoint.jpar @@ -0,0 +1,29 @@ +{ + "host": "localhost", + "sparql11protocol": { + "protocol": "http", + "port": 9999, + "query": { + "path": "/blazegraph/namespace/kb/sparql", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/blazegraph/namespace/kb/sparql", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9443, + "path": "/subscribe" + }, + "wss": { + + } + } + } +} \ No newline at end of file diff --git a/client-api/sepa.jks b/client-api/src/main/resources/sepa.jks similarity index 100% rename from client-api/sepa.jks rename to client-api/src/main/resources/sepa.jks diff --git a/client-api/src/test/resources/sepa.jks b/client-api/src/test/resources/sepa.jks new file mode 100644 index 0000000000000000000000000000000000000000..4b4d14d0ad13840ce7960e7ab898fe46f439e0a8 GIT binary patch literal 2317 zcmd6o={wX58^>q0%-Gkc$&g+64bj9wwlpF8n&n{ZYi0;%TFlsGElt)ajU|b)B}&=F zi0n(Yk;qyiOB|i&d9LgH3D1lB#pk}iFYfDmeXsjISUFe$fk2R>0{>IY1ot2pU-$4M zb|`om_8kO*LjW@D0N`Lk$T5LoU|9$^7z_u&$grjPQpWeNp)+?%B7Tk08!AJ^+Hq_nwwu41kD{bxSZf**5y zn>%ua<_UeH{llWfw7Q!$6^E+5QQDRLC&mT7v$%A(I-1 zLQ-3yZkI9-6U|;Mq!JuIjOHztxX(Qz&GbGrohh(0-HY?#(lNll#b+d#4SuVpwMyvh zmRn=DoEsdry$x&L3QH%wEkGBoB-it696AWclkIsx{(HRHsiI*eNV^G!l@G}AH-Tu zex=n!$LH&xN7Z0?_C_r4P*tqLEEjB^raCzd( zPBOIBrCDp{IftlHXE$5qm(HAar;>$$Y+~IId#I>5$|t|vO+_bkWK(1?fV2F9IKNuz z=l<$}fs11)x|)-pSizvfbB0@75cx;RA?;bTCz)mN{T(i1EF$ba97x@S+0-`5Q(|Pk zY6Y3Nb>uu&clwyBxa^UA<0Anjs(BN?_F8wZAXZ_fU%es~mVaE?YqvDf49V12Tz5k! zr|S90_!284`qTQmgIk;|_d*#y{Q7I}Q^p_Mg{!CAqiKm>{?L^hy1Ge;l#Y&y z-13^FH|nC44s08Vj~OG`3IHP776q%U3ob5`VH328KmWK;VNh3%b!)*OG$(%yGp-eV z(JW@7@hJqI#cYM#!zt+%TJRs|h-w)FB2 z@Fbwk-3jh4!ERm{c|h*B0Lm+FVXAFzW`x!cbq^q-O@jm7+zEsuEn53X=1p{WBZdUK z11AO9F=qi)j50=91*5ELe}q^-6~O)%{|`4NgT?-B>(SSNlEIuHfDA@J$Y3yNO`hbD zJjrK}-o5}Ej? z?;MlMy_$OAU<>C^LlQbQ_xvl#DO`krY*t@?*-7199v^|a@YQNBO`F&l<*KRU7b;CQ zgoZ*W5|o;-@4>e1n|i1j-MWevNwi{N(s@mh7n*U2Z9*qbC;Z%Zo>IuO3dG+__D6<{ z>W^Pidoa46Bia~k`Lf%QEV8;0{uFzSLhZ+y-B<&tgHHw8EYq8_p3awc)=Qe!_y3@s zrk5$ypK-Wx`z+_u``fi%HiMz60#Mu0NgZtDc!fCw0YB|uq`0=1>g$4~K%ig{_>m~^ zpQNBTU;;3{q5dueKggIB4~YoV7!%@~pIIONw~0sDJTegslB6vI!#?!&xNmy!H153S z>ynAdlEbpOvs9S=%5tqZ?8R`;T;0cs@rW-Ijjyqmq$+imsU7TW+oAyfEh2EtdQWO2 ztjeG9Q0nsH$)=U29oxuz?2jaHbD3?G_|Wwm3mpr`d@JI5X2Y*gt$LK4Do6N7BF=RS zPc3iv$jzO;4qEg3$?O?ut3EYJPqU&is)~?pPQ!I%*xieoA;*Xz_{p}FB=ak)L!N2- zi7NvcP1CV!#*el2y<5JuV`h`H@VIs!CB=zT(SmYPQbC3g89Hg=La&mLkfH=>Oi1ck zHr8}+igV5C*!78FGZm+el_AD7-4dbFa80vAG-Z>%S!3j+Qx Date: Fri, 7 Sep 2018 09:13:34 +0200 Subject: [PATCH 40/76] Update .travis.yml mvn verify -d testConfiguration=sepatest.jsap Settings for "not secure" testing --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6149ffaa..eb74eb7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ jobs: - sleep 30 - cd ../.. script: - - mvn verify + - mvn verify -d testConfiguration=sepatest.jsap - stage: deploy env: - secure: UBX6IAsuQFTWSlR5n/75KLa1bPFBFNg2K50d59mgrTPSfPt9zWyW04qCrirR8MGCO6SRBs1PZTR4odCwn3gMRNU388IVBauNiBw3nFkHUWeW61GXFIbZcUCwmzZasfhFlqtq9BAb+jhcsgM1EQQtz9Y6ZxgyA0O3bCEv2oJsCbdR74qGR1rfpLhGy8jUMzMWGEZ/MWyRM/HyQYXiUiQBcAdqwZVImc7k9+sZmo5gSchI0bhl/DZlzE/PvVGMh+asypYsSpOIF7HijLNVPy7pzXRWmnA4aTX2s93YSZaaB+Ux1VUPClGfzk+ZaopdzqC7L00+cBBd7r6vEUftbAoL/nIvti0TvL2onhtIGPeA5pozroUxAOgH4/ImndVm/ZZoawp2psix4wMC8yVg94hxBfZN/HU96Rh6ckHY4u5faLE7yEBmTEu9G4nz3pZnY8bj+sJBwXq9J1UB0FjTCnivZTAFQWMIcG1UBNhrnvrh/mBhnLr4sXnK5x1Y9ufzJlcVIWmnWUcEjWRRc26r9AVAxrPMRx0t2bY+Hqr/ggM2+zlKMIf/nYI5JtDbseXdldIJeGT4bVMq2q9I0TF9Kg4oSznyn5dO/IfN1wb2M67ZJPpHpQL1wX0HsLgj/5z09wBo7DLO4A24c5+iP32SuRORGmzJxeAsAnw+OKK7Y3IHgHU= @@ -39,4 +39,4 @@ jobs: on: tags: true repo: arces-wot/SEPA - if: tag IS present \ No newline at end of file + if: tag IS present From b35cf6823642d15423029cd530f3ebd1e4ac6969 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 7 Sep 2018 09:19:45 +0200 Subject: [PATCH 41/76] Update .travis.yml Added secure stage integration test --- .travis.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.travis.yml b/.travis.yml index eb74eb7e..7a5a7efa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,19 @@ jobs: - cd ../.. script: - mvn verify -d testConfiguration=sepatest.jsap + - stage: integrationSecure + before_script: + - docker run --name blazegraph -d -p 9999:9999 nawer/blazegraph:2.1.1 + - sleep 30 + - wget http://localhost:9999/blazegraph/namespace/kb/sparql + - mvn package + - cd engine/target + - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar + - java -jar engine-0-SNAPSHOT.jar -secure > log.txt & + - sleep 30 + - cd ../.. + script: + - mvn verify -d testConfiguration=sepatest-secure.jsap - stage: deploy env: - secure: UBX6IAsuQFTWSlR5n/75KLa1bPFBFNg2K50d59mgrTPSfPt9zWyW04qCrirR8MGCO6SRBs1PZTR4odCwn3gMRNU388IVBauNiBw3nFkHUWeW61GXFIbZcUCwmzZasfhFlqtq9BAb+jhcsgM1EQQtz9Y6ZxgyA0O3bCEv2oJsCbdR74qGR1rfpLhGy8jUMzMWGEZ/MWyRM/HyQYXiUiQBcAdqwZVImc7k9+sZmo5gSchI0bhl/DZlzE/PvVGMh+asypYsSpOIF7HijLNVPy7pzXRWmnA4aTX2s93YSZaaB+Ux1VUPClGfzk+ZaopdzqC7L00+cBBd7r6vEUftbAoL/nIvti0TvL2onhtIGPeA5pozroUxAOgH4/ImndVm/ZZoawp2psix4wMC8yVg94hxBfZN/HU96Rh6ckHY4u5faLE7yEBmTEu9G4nz3pZnY8bj+sJBwXq9J1UB0FjTCnivZTAFQWMIcG1UBNhrnvrh/mBhnLr4sXnK5x1Y9ufzJlcVIWmnWUcEjWRRc26r9AVAxrPMRx0t2bY+Hqr/ggM2+zlKMIf/nYI5JtDbseXdldIJeGT4bVMq2q9I0TF9Kg4oSznyn5dO/IfN1wb2M67ZJPpHpQL1wX0HsLgj/5z09wBo7DLO4A24c5+iP32SuRORGmzJxeAsAnw+OKK7Y3IHgHU= From 8470b7e144510631466c11ea7c3e88b776b9b4c3 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 7 Sep 2018 09:21:32 +0200 Subject: [PATCH 42/76] Update .travis.yml mvn option changed from -d to -D --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a5a7efa..7cee58dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ jobs: - sleep 30 - cd ../.. script: - - mvn verify -d testConfiguration=sepatest.jsap + - mvn verify -D testConfiguration=sepatest.jsap - stage: integrationSecure before_script: - docker run --name blazegraph -d -p 9999:9999 nawer/blazegraph:2.1.1 @@ -32,7 +32,7 @@ jobs: - sleep 30 - cd ../.. script: - - mvn verify -d testConfiguration=sepatest-secure.jsap + - mvn verify -D testConfiguration=sepatest-secure.jsap - stage: deploy env: - secure: UBX6IAsuQFTWSlR5n/75KLa1bPFBFNg2K50d59mgrTPSfPt9zWyW04qCrirR8MGCO6SRBs1PZTR4odCwn3gMRNU388IVBauNiBw3nFkHUWeW61GXFIbZcUCwmzZasfhFlqtq9BAb+jhcsgM1EQQtz9Y6ZxgyA0O3bCEv2oJsCbdR74qGR1rfpLhGy8jUMzMWGEZ/MWyRM/HyQYXiUiQBcAdqwZVImc7k9+sZmo5gSchI0bhl/DZlzE/PvVGMh+asypYsSpOIF7HijLNVPy7pzXRWmnA4aTX2s93YSZaaB+Ux1VUPClGfzk+ZaopdzqC7L00+cBBd7r6vEUftbAoL/nIvti0TvL2onhtIGPeA5pozroUxAOgH4/ImndVm/ZZoawp2psix4wMC8yVg94hxBfZN/HU96Rh6ckHY4u5faLE7yEBmTEu9G4nz3pZnY8bj+sJBwXq9J1UB0FjTCnivZTAFQWMIcG1UBNhrnvrh/mBhnLr4sXnK5x1Y9ufzJlcVIWmnWUcEjWRRc26r9AVAxrPMRx0t2bY+Hqr/ggM2+zlKMIf/nYI5JtDbseXdldIJeGT4bVMq2q9I0TF9Kg4oSznyn5dO/IfN1wb2M67ZJPpHpQL1wX0HsLgj/5z09wBo7DLO4A24c5+iP32SuRORGmzJxeAsAnw+OKK7Y3IHgHU= From fc3a42683752b543aae61dae29c5546c22bb532b Mon Sep 17 00:00:00 2001 From: reluc Date: Fri, 7 Sep 2018 11:46:48 +0200 Subject: [PATCH 43/76] Modify configuration provider Removed some copy&paste classes. Add secure system property in the configuration provider. Tests can be run with the secure property es. mv verify -D secure=true --- .../websocket/ConfigurationProvider.java | 22 ------------------- .../ITWebSocketSubscriptionProtocol.java | 1 + .../security/ConfigurationProvider.java | 22 ------------------- .../security/ITSEPASecurityManager.java | 3 ++- 4 files changed, 3 insertions(+), 45 deletions(-) delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java deleted file mode 100644 index c116817b..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import java.net.URL; - -public class ConfigurationProvider { - - public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - JSAP result; - final String configuaration = System.getProperty("testConfiguration"); - if( configuaration != null){ - result = new JSAP(configuaration); - }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); - result = new JSAP(config.getPath()); - } - return result; - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 10514531..625e4f5d 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -3,6 +3,7 @@ import java.net.URISyntaxException; import java.util.concurrent.atomic.AtomicLong; +import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.After; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java deleted file mode 100644 index 40676e08..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java +++ /dev/null @@ -1,22 +0,0 @@ -package it.unibo.arces.wot.sepa.commons.security; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import java.net.URL; - -public class ConfigurationProvider { - - public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - JSAP result; - final String configuaration = System.getProperty("testConfiguration"); - if( configuaration != null){ - result = new JSAP(configuaration); - }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); - result = new JSAP(config.getPath()); - } - return result; - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java index 0fe401bf..57232da6 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -1,5 +1,6 @@ package it.unibo.arces.wot.sepa.commons.security; +import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import org.junit.BeforeClass; import org.junit.Test; @@ -20,7 +21,7 @@ public class ITSEPASecurityManager { @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException { - JSAP app = ConfigurationProvider.GetTestEnvConfiguration(); + JSAP app = ConfigurationProvider.GetTestEnvConfiguration(); if (app.isSecure()) sm = new SEPASecurityManager(app.getAuthenticationProperties()); } From 32b46ac6b256becc250ed0c3f575061d68cda937 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 7 Sep 2018 11:51:22 +0200 Subject: [PATCH 44/76] Configuration provider resource path --- .../java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index 29c59906..03a8ffa9 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -14,7 +14,7 @@ public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEP if( configuaration != null){ result = new JSAP(configuaration); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest-secure.jsap"); + URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); result = new JSAP(config.getPath()); } return result; From 204f25fe221ff05e4f9707e21ce2d288c3bef478 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 12 Sep 2018 16:35:33 +0200 Subject: [PATCH 45/76] Dependability manager refactory - Fixed a bug occurring on broken subscriptions. - All JUnit tests passed. - Ready to be release as 0.9.5. --- client-api/.gitignore | 1 + .../wot/sepa/api/ConfigurationProvider.java | 22 +- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 2 +- .../unibo/arces/wot/sepa/api/Subscriber.java | 3 + .../websocket/ConfigurationProvider.java | 29 ++ .../websocket/ITSEPAWebsocketClient.java | 52 ++- .../protocol/websocket/ITWebSocketClient.java | 36 ++- .../ITWebSocketSubscriptionProtocol.java | 295 ++++++++---------- .../security/ConfigurationProvider.java | 29 ++ .../security/ITSEPASecurityManager.java | 11 +- .../engine/bean/SubscribeProcessorBeans.java | 10 +- .../arces/wot/sepa/engine/core/Engine.java | 12 +- .../wot/sepa/engine/core/ResponseHandler.java | 16 +- .../dependability/AuthorizationManager.java | 2 +- .../dependability/DependabilityManager.java | 54 ++-- .../processing/SubscribeProcessingThread.java | 99 +++--- .../http/handler/SPARQL11ResponseHandler.java | 3 +- .../websocket/SecureWebsocketServer.java | 5 +- .../websocket/WebsocketEventHandler.java | 27 +- .../protocol/websocket/WebsocketServer.java | 50 +-- .../wot/sepa/engine/scheduling/Scheduler.java | 127 +++++--- 21 files changed, 506 insertions(+), 379 deletions(-) create mode 100644 client-api/.gitignore create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java diff --git a/client-api/.gitignore b/client-api/.gitignore new file mode 100644 index 00000000..c47e15b4 --- /dev/null +++ b/client-api/.gitignore @@ -0,0 +1 @@ +/endpoint.jpar diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java index a9c6b2ef..a53998a4 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java @@ -9,17 +9,21 @@ public class ConfigurationProvider { public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - JSAP result; - final String configuaration = System.getProperty("testConfiguration"); - if( configuaration != null){ - result = new JSAP(configuaration); + String jsapFileName = null; + + if( System.getProperty("testConfiguration") != null){ + jsapFileName = System.getProperty("testConfiguration"); + } + else if (System.getenv("testConfiguration") != null) { + jsapFileName = System.getenv("testConfiguration"); }else if (System.getProperty("secure") != null){ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest-secure.jsap"); - result = new JSAP(config.getPath()); + jsapFileName = System.getenv("sepatest-secure.jsap"); }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("sepatest.jsap"); - result = new JSAP(config.getPath()); + jsapFileName = System.getenv("sepatest.jsap"); } - return result; + + URL config = Thread.currentThread().getContextClassLoader().getResource(jsapFileName); + + return new JSAP(config.getPath()); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 4e9cb76b..f0fad979 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -236,7 +236,7 @@ public void Subscribe3xN() for (Subscriber sub : subscribers) { sub.subscribe(); - Thread.sleep(500); + //Thread.sleep(500); } sync.waitSubscribes(subscribers.size()); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java index 8e1199b9..b5e5107b 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -5,6 +5,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; +import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java new file mode 100644 index 00000000..5d77b1d0 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java @@ -0,0 +1,29 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import java.net.URL; + +public class ConfigurationProvider { + + public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { + String jsapFileName = null; + + if( System.getProperty("testConfiguration") != null){ + jsapFileName = System.getProperty("testConfiguration"); + } + else if (System.getenv("testConfiguration") != null) { + jsapFileName = System.getenv("testConfiguration"); + }else if (System.getProperty("secure") != null){ + jsapFileName = System.getenv("sepatest-secure.jsap"); + }else{ + jsapFileName = System.getenv("sepatest.jsap"); + } + + URL config = Thread.currentThread().getContextClassLoader().getResource(jsapFileName); + + return new JSAP(config.getPath()); + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java index a6a92629..cdd71344 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java @@ -8,30 +8,64 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.junit.BeforeClass; import org.junit.Test; +import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.protocols.websocket.SEPAWebsocketClient; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; public class ITSEPAWebsocketClient implements ISubscriptionHandler { protected final Logger logger = LogManager.getLogger(); + protected static JSAP properties = null; + protected static String url = null; - @Test(timeout=10000) + SEPAWebsocketClient client = null; + + @BeforeClass + public static void init() throws Exception { + properties = ConfigurationProvider.GetTestEnvConfiguration(); + if(properties.isSecure()) { + int port = properties.getSubscribePort(); + if (port == -1) + url = "wss://"+properties.getDefaultHost()+properties.getSubscribePath(); + else + url = "wss://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); + } + else { + int port = properties.getSubscribePort(); + if (port == -1) + url = "ws://"+properties.getDefaultHost()+properties.getSubscribePath(); + else + url = "ws://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); + } + } + + @Test(timeout = 10000) public void Connect() throws InterruptedException, URISyntaxException, IOException, SEPASecurityException { - for (int i=0; i < 100; i++) { - SEPASecurityManager sm = new SEPASecurityManager(); - SEPAWebsocketClient client = new SEPAWebsocketClient(new URI("wss://localhost:9443/secure/subscribe"),this,sm.getSSLSocket()); - assertFalse("Failed to connect",!client.connectBlocking()); + for (int i = 0; i < 100; i++) { + if(properties.isSecure()) { + SEPASecurityManager sm = new SEPASecurityManager(); + client = new SEPAWebsocketClient(new URI(url), this, + sm.getSSLSocket()); + } + else { + client = new SEPAWebsocketClient(new URI(url), this); + } + + assertFalse("Failed to connect", !client.connectBlocking()); + } } @Override public void onSemanticEvent(Notification notify) { - logger.debug("@onSemanticEvent: "+notify); + logger.debug("@onSemanticEvent: " + notify); } @Override @@ -41,16 +75,16 @@ public void onBrokenConnection() { @Override public void onError(ErrorResponse errorResponse) { - logger.debug("@onError: "+errorResponse); + logger.debug("@onError: " + errorResponse); } @Override public void onSubscribe(String spuid, String alias) { - logger.debug("@onSubscribe: "+spuid+ " alias: "+alias); + logger.debug("@onSubscribe: " + spuid + " alias: " + alias); } @Override public void onUnsubscribe(String spuid) { - logger.debug("@onUnsubscribe: "+spuid); + logger.debug("@onUnsubscribe: " + spuid); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java index 2ae2fc86..366b2bbc 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java @@ -8,16 +8,20 @@ import org.apache.logging.log4j.Logger; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; - +import org.junit.BeforeClass; import org.junit.Test; +import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; import static org.junit.Assert.assertFalse; public class ITWebSocketClient { protected final Logger logger = LogManager.getLogger(); + protected static JSAP properties = null; + protected static String url = null; class WebsocketTest extends WebSocketClient { @@ -47,13 +51,37 @@ public void onError(Exception ex) { } + @BeforeClass + public static void init() throws Exception { + properties = ConfigurationProvider.GetTestEnvConfiguration(); + if(properties.isSecure()) { + int port = properties.getSubscribePort(); + if (port == -1) + url = "wss://"+properties.getDefaultHost()+properties.getSubscribePath(); + else + url = "wss://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); + } + else { + int port = properties.getSubscribePort(); + if (port == -1) + url = "ws://"+properties.getDefaultHost()+properties.getSubscribePath(); + else + url = "ws://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); + } + } + @Test(timeout=20000) public void Connect() throws URISyntaxException, InterruptedException, SEPASecurityException, IOException { - SEPASecurityManager sm = new SEPASecurityManager(); + for (int i=0; i < 100; i++) { - WebsocketTest client = new WebsocketTest(new URI("wss://localhost:9443/secure/subscribe")); - client.setSocket(sm.getSSLSocket()); + WebsocketTest client = new WebsocketTest(new URI(url)); + + if (properties.isSecure()) { + SEPASecurityManager sm = new SEPASecurityManager(); + client.setSocket(sm.getSSLSocket()); + } + assertFalse("Failed to connect",!client.connectBlocking()); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 625e4f5d..563dd46d 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -1,9 +1,9 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; -import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.After; @@ -11,6 +11,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; @@ -29,21 +30,15 @@ public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { protected final Logger logger = LogManager.getLogger(); private static JSAP app = null; - private WebsocketSubscriptionProtocol client = null; private static SEPASecurityManager sm = null; - - private static AtomicLong events = new AtomicLong(0); private static AtomicLong subscribes = new AtomicLong(0); - private static AtomicLong brokens = new AtomicLong(0); - private String spuid = null; - private static final Object spuidMutex = new Object(); - + @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException { app = ConfigurationProvider.GetTestEnvConfiguration(); - + if (app.isSecure()) { sm = new SEPASecurityManager(app.getAuthenticationProperties()); sm.register("SEPATest"); @@ -59,192 +54,168 @@ public void before() } else client = new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), this); - - events.set(0); + subscribes.set(0); - brokens.set(0); } - + @After public void after() { - + } @Test(timeout = 5000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { SubscribeRequest request; if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); } else - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), null, 5000); + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), null, 5000); logger.debug(request); - + client.subscribe(request); - - while(subscribes.get() != 1) { - synchronized(subscribes) { + + while (subscribes.get() != 1) { + synchronized (subscribes) { try { subscribes.wait(); } catch (InterruptedException e) { - + } } } - + client.close(); - - assertFalse("Failed to subscribe",subscribes.get() != 1); - } -// @Test(timeout = 20000) -// public void BrokenSockets() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { -// int n = 5; -// -// SubscribeRequest request; -// ArrayList threadPoll = new ArrayList(); -// Thread th; -// -//// for (int i = 0; i < n; i++) { -//// if (app.isSecure()) { -//// request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), -//// app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); -//// } else -//// request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "all", app.getDefaultGraphURI("ALL"), -//// app.getNamedGraphURI("ALL"), null, 5000); -//// th = new Thread(new ITSEPAWebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, -//// request,this)); -//// threadPoll.add(th); -//// th.start(); -//// -//// if (app.isSecure()) { -//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), -//// app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); -//// } else -//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "random", app.getDefaultGraphURI("RANDOM"), -//// app.getNamedGraphURI("RANDOM"), null, 5000); -//// th = new Thread(new ITSEPAWebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, -//// request,this)); -//// threadPoll.add(th); -//// th.start(); -//// -//// if (app.isSecure()) { -//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", -//// app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), sm.getAuthorizationHeader(), -//// 5000); -//// } else -//// request = new SubscribeRequest(app.getSPARQLQuery("RANDOM1"), "random1", -//// app.getDefaultGraphURI("RANDOM1"), app.getNamedGraphURI("RANDOM1"), null, 5000); -//// th = new Thread(new ITSEPAWebsocketClient(app.getDefaultHost(), app.getSubscribePort(), app.getSubscribePath(), sm, -//// request,this)); -//// threadPoll.add(th); -//// th.start(); -//// } -// -// while(subscribes.get() != n * 3) { -// synchronized(subscribes) { -// try { -// subscribes.wait(); -// } catch (InterruptedException e) { -// -// } -// } -// } -// -// assertFalse("Failed to subscribe",subscribes.get() != n * 3); -// -// for (Thread th1 : threadPoll) { -// try { -// th1.join(); -// } catch (InterruptedException e) { -// -// } -// } -// -// while(events.get() != n * 3) { -// synchronized(events) { -// try { -// events.wait(); -// } catch (InterruptedException e) { -// -// } -// } -// } -// -// assertFalse("Failed to receive all first notifications",events.get() != n * 3); -// -// while(brokens.get() != n * 3) { -// synchronized(brokens) { -// try { -// brokens.wait(); -// } catch (InterruptedException e) { -// -// } -// } -// } -// -// assertFalse("Failed to receive all broken notifications",brokens.get() != n * 3); -// } + assertFalse("Failed to subscribe", subscribes.get() != 1); + } @Test(timeout = 5000) public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - int n = 100; - + int n = 500; + SubscribeRequest request; if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 500); + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 500); } else - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), null, 500); + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), null, 500); for (int i = 0; i < n; i++) { logger.debug(request); client.subscribe(request); } - - while(subscribes.get() < n) { - synchronized(subscribes) { + + while (subscribes.get() < n) { + synchronized (subscribes) { + try { + subscribes.wait(); + } catch (InterruptedException e) { + + } + } + } + + assertFalse("Failed to subscribe", subscribes.get() != n); + + client.close(); + } + + @Test(timeout = 10000) + public void SubscribeMxN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + int n = 50; + int m = 20; + + ArrayList clients = new ArrayList(); + + for (int i = 0; i < m; i++) { + if (app.isSecure()) { + clients.add(new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), + app.getSubscribePath(), sm, this)); + } else + clients.add(new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), + app.getSubscribePath(), this)); + } + + for (int i = 0; i < m; i++) { + new Subscriber(clients.get(i),n).start(); + } + + while (subscribes.get() < n * m) { + synchronized (subscribes) { try { subscribes.wait(); } catch (InterruptedException e) { - + } } } - - assertFalse("Failed to subscribe",subscribes.get() != n); + + assertFalse("Failed to subscribe", subscribes.get() != n * m); client.close(); } + class Subscriber extends Thread { + private WebsocketSubscriptionProtocol client; + private int n; + + public Subscriber(WebsocketSubscriptionProtocol client, int n) { + this.client = client; + this.n = n; + } + + public void run() { + for (int j = 0; j < n; j++) { + SubscribeRequest request = null; + if (app.isSecure()) { + try { + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 500); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + } else + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), null, 500); + logger.debug(request); + try { + client.subscribe(request); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); + } + } + } + } + @Test(timeout = 5000) public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { SubscribeRequest request; if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), sm.getAuthorizationHeader(), 5000); + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); } else - request = new SubscribeRequest(app.getSPARQLQuery("ALL"), "TEST_ALL_SUB", app.getDefaultGraphURI("ALL"), - app.getNamedGraphURI("ALL"), null, 5000); + request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), + app.getNamedGraphURI("RANDOM"), null, 5000); spuid = null; client.subscribe(request); - - while(subscribes.get() != 1) { - synchronized(subscribes) { + + while (subscribes.get() != 1) { + synchronized (subscribes) { try { subscribes.wait(); } catch (InterruptedException e) { - + } } } - - assertFalse("Failed to subscribe",subscribes.get() != 1); - + + assertFalse("Failed to subscribe", subscribes.get() != 1); + UnsubscribeRequest unsub; if (app.isSecure()) { unsub = new UnsubscribeRequest(spuid, sm.getAuthorizationHeader(), 5000); @@ -253,69 +224,53 @@ public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecuri } client.unsubscribe(unsub); - - while(subscribes.get() != 0) { - synchronized(subscribes) { + + while (subscribes.get() != 0) { + synchronized (subscribes) { try { subscribes.wait(); } catch (InterruptedException e) { - + } } } - - assertFalse("Failed to unsubscribe",subscribes.get() != 0); + + assertFalse("Failed to unsubscribe", subscribes.get() != 0); client.close(); } @Override public void onSemanticEvent(Notification notify) { - logger.debug("@onSemanticEvent: "+notify); - - synchronized(events) { - events.set(events.get()+1); - events.notify(); - } - - logger.debug("Number of events: "+events.get()); + logger.debug("@onSemanticEvent: " + notify); } @Override public void onBrokenConnection() { logger.debug("@onBrokenConnection"); - - synchronized(brokens) { - brokens.set(brokens.get()+1); - brokens.notify(); - } } @Override public void onError(ErrorResponse errorResponse) { - logger.error("@onError: "+errorResponse); + logger.error("@onError: " + errorResponse); } @Override public void onSubscribe(String spuid, String alias) { - logger.debug("@onSubscribe: "+spuid+" alias: "+alias); - - synchronized(subscribes) { - subscribes.set(subscribes.get()+1); - subscribes.notify(); - } - - synchronized (spuidMutex) { + logger.debug("@onSubscribe: " + spuid + " alias: " + alias); + + synchronized (subscribes) { this.spuid = spuid; - spuidMutex.notify(); + subscribes.set(subscribes.get() + 1); + subscribes.notify(); } } @Override public void onUnsubscribe(String spuid) { - logger.debug("@onUnsubscribe "+spuid); - synchronized(subscribes) { - subscribes.set(subscribes.get()-1); + logger.debug("@onUnsubscribe " + spuid); + synchronized (subscribes) { + subscribes.set(subscribes.get() - 1); subscribes.notify(); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java new file mode 100644 index 00000000..baf0d712 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java @@ -0,0 +1,29 @@ +package it.unibo.arces.wot.sepa.commons.security; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import java.net.URL; + +public class ConfigurationProvider { + + public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { + String jsapFileName = null; + + if( System.getProperty("testConfiguration") != null){ + jsapFileName = System.getProperty("testConfiguration"); + } + else if (System.getenv("testConfiguration") != null) { + jsapFileName = System.getenv("testConfiguration"); + }else if (System.getProperty("secure") != null){ + jsapFileName = System.getenv("sepatest-secure.jsap"); + }else{ + jsapFileName = System.getenv("sepatest.jsap"); + } + + URL config = Thread.currentThread().getContextClassLoader().getResource(jsapFileName); + + return new JSAP(config.getPath()); + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java index 57232da6..80944ae7 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -1,9 +1,9 @@ package it.unibo.arces.wot.sepa.commons.security; -import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import org.junit.BeforeClass; import org.junit.Test; +import it.unibo.arces.wot.sepa.api.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -15,18 +15,21 @@ public class ITSEPASecurityManager { private static SEPASecurityManager sm = null; - + private static JSAP app = null; + private String testId = "SEPATest"; private String notAllowedId = "IamNotAllowedToRegister"; @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException { - JSAP app = ConfigurationProvider.GetTestEnvConfiguration(); + app = ConfigurationProvider.GetTestEnvConfiguration(); if (app.isSecure()) sm = new SEPASecurityManager(app.getAuthenticationProperties()); } @Test(timeout = 2000) public void Register() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + if (sm == null && !app.isSecure()) return; + Response ret = sm.register(testId); assertFalse(String.valueOf(ret),ret.isError()); @@ -36,6 +39,8 @@ public void Register() throws SEPAProtocolException, SEPASecurityException, SEPA @Test(timeout = 15000) public void GetAuthorizationHeader() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { + if (sm == null && !app.isSecure()) return; + Response ret = sm.register(testId); assertFalse(String.valueOf(ret),ret.isError()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java index f0ebb11b..70f9dc29 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java @@ -44,7 +44,7 @@ public static long getUpdateRequests() { return requests; } - public static long getSPUs_current() { + public synchronized static long getSPUs_current() { return activeSPUs; } @@ -52,7 +52,7 @@ public static long getSPUs_max() { return maxActiveSPUs; } - public static void setActiveSPUs(long n) { + public synchronized static void setActiveSPUs(long n) { activeSPUs = n; if (activeSPUs > maxActiveSPUs) maxActiveSPUs = activeSPUs; } @@ -131,16 +131,16 @@ public static void setSPUProcessingTimeout(int t) { SPUProcessingTimeout = t; } - public static void registerHandler() { + public synchronized static void addSubscriber() { subscribers++; if (subscribers > subscribers_max) subscribers_max = subscribers; } - public static void unregisterHandler() { + public synchronized static void removeSubscriber() { subscribers--; } - public static int getSubscribers() { + public synchronized static int getSubscribers() { return subscribers; } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 9cece7d1..53f8355d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -42,7 +42,6 @@ import it.unibo.arces.wot.sepa.engine.bean.EngineBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; -import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; import it.unibo.arces.wot.sepa.engine.processing.Processor; @@ -87,9 +86,6 @@ public class Engine implements EngineMBean { // Oauth 2.0 Authorization Server private AuthorizationManager oauth; - // Dependability manager - private DependabilityManager dependabilityMng; - // JKS Credentials private String storeName = "sepa.jks"; private String storePassword = "sepa2017"; @@ -243,9 +239,6 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException scheduler = new Scheduler(properties); scheduler.start(); - // Dependability manager - dependabilityMng = new DependabilityManager(scheduler.getSchedulerQueue()); - // SEPA Processor try { processor = new Processor(endpointProperties, properties, scheduler.getSchedulerQueue()); @@ -316,8 +309,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException System.out.println("----------------------"); if (!properties.isSecure()) { - wsServer = new WebsocketServer(properties.getWsPort(), properties.getSubscribePath(), scheduler, - dependabilityMng); + wsServer = new WebsocketServer(properties.getWsPort(), properties.getSubscribePath(), scheduler); wsServer.start(); synchronized (wsServer) { try { @@ -328,7 +320,7 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException } } else { wssServer = new SecureWebsocketServer(properties.getWssPort(), - properties.getSecurePath() + properties.getSubscribePath(), scheduler, oauth, dependabilityMng); + properties.getSecurePath() + properties.getSubscribePath(), scheduler, oauth); wssServer.start(); synchronized (wssServer) { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java index 4d6e048f..6a46b1f1 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java @@ -1,8 +1,20 @@ package it.unibo.arces.wot.sepa.engine.core; +import java.util.UUID; + import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.response.Response; -public interface ResponseHandler { - public void sendResponse(Response response) throws SEPAProtocolException; +public abstract class ResponseHandler { + private final UUID uuid; + + public ResponseHandler() { + uuid = UUID.randomUUID(); + } + + public abstract void sendResponse(Response response) throws SEPAProtocolException; + + public final UUID getUUID() { + return uuid; + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java index 8e5397d2..e5046de4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java @@ -580,7 +580,7 @@ public synchronized Response getToken(String encodedCredentials) { @param accessToken the JWT token to be validate according to points 4-9 */ public synchronized Response validateToken(String accessToken) { - logger.debug("Validate token"); + logger.trace("Validate token"); //Parse and verify the token SignedJWT signedJWT = null; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java index b7f9759c..73156cde 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.UUID; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -13,7 +14,7 @@ public class DependabilityManager { private static final Logger logger = LogManager.getLogger(); // Active subscriptions - private static final HashMap> subscriptions = new HashMap>(); + private static final HashMap> subscriptions = new HashMap>(); // Scheduler queue private final SchedulerQueue schedulerQueue; @@ -22,47 +23,48 @@ public DependabilityManager(SchedulerQueue schedulerQueue) { this.schedulerQueue = schedulerQueue; } - public synchronized void onSubscribe(Integer hash, String spuid) { - logger.debug("@onSubscribe: " + hash + " SPUID: " + spuid); - - if (!subscriptions.containsKey(hash)) - subscriptions.put(hash, new ArrayList()); - subscriptions.get(hash).add(spuid); + public synchronized void onSubscribe(UUID uuid, String spuid) { + if (uuid == null || spuid == null) { + logger.error("Some values are null. UUID: "+uuid+" SPUID: "+spuid); + return; + } - logger.debug("Active subscriptions: "+subscriptions.size()); + if (!subscriptions.containsKey(uuid)) + subscriptions.put(uuid, new ArrayList()); + subscriptions.get(uuid).add(spuid); + + logger.debug("@onSubscribe Subscriptions: " + subscriptions.size()+" Handlers (" + uuid + "): " + subscriptions.get(uuid).size()); } - public synchronized void onUnsubscribe(Integer hash, String spuid) { - logger.debug("@onUnsubscribe: " + hash + " SPUID: " + spuid); + public synchronized void onUnsubscribe(UUID uuid, String spuid) { + logger.debug("@onUnsubscribe: " + uuid + " SPUID: " + spuid); - subscriptions.get(hash).remove(spuid); - if (subscriptions.get(hash).isEmpty()) - subscriptions.remove(hash); + subscriptions.get(uuid).remove(spuid); + if (subscriptions.get(uuid).isEmpty()) + subscriptions.remove(uuid); logger.debug("Active subscriptions: "+subscriptions.size()); } - public synchronized void onBrokenSocket(Integer hash) { - logger.debug("@onBrokenSocket: " + hash); - - if (!subscriptions.containsKey(hash)) return; + public synchronized void onBrokenSubscription(UUID uuid) { + if (!subscriptions.containsKey(uuid)) { + logger.warn("Broken socket not registered: "+uuid); + return; + } - logger.debug(String.format("Broken socket with active subscriptions: %d", subscriptions.get(hash).size())); + logger.debug(String.format("@onBrokenSocket: " + uuid +" with active subscriptions: %d", subscriptions.get(uuid).size())+" (sockets opened: "+subscriptions.size()+")"); // Kill all SPUs - for (String spuid : subscriptions.get(hash)) { - logger.debug("Schedule request to kill SPU: " + spuid); + for (String spuid : subscriptions.get(uuid)) { + logger.trace("Schedule request to kill SPU: " + spuid); schedulerQueue.killSpuid(spuid); } // Remove subscriptions - subscriptions.remove(hash); - - logger.debug("Active subscriptions: "+subscriptions.size()); - + subscriptions.remove(uuid); } - public void onError(Integer hash, ErrorResponse error) { - logger.error("Subscription:" + hash + " error:" + error); + public void onError(UUID uuid, ErrorResponse error) { + logger.error("Subscription:" + uuid + " error:" + error); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java index c6812b29..7308eacc 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java @@ -27,7 +27,7 @@ class SubscribeProcessingThread extends Thread implements SubscribeProcessingThr // Maps private static final HashMap> activeSpus = new HashMap>(); private static final HashMap spuids = new HashMap(); - private static final HashMap handlers = new HashMap(); + private static final HashMap subscribers = new HashMap(); private static final HashMap sequenceNumbers = new HashMap(); // Broken SPUs disposer @@ -43,9 +43,10 @@ public void run() { while (processor.isRunning()) { try { String spuid = processor.getSchedulerQueue().waitSpuid2Kill(); - synchronized (activeSpus) { - unregisterHandler(spuids.get(spuid), spuid); - } +// synchronized (activeSpus) { +// unregisterHandler(spuids.get(spuid), spuid); +// } + unsubscribe(spuid); } catch (InterruptedException e) { return; } @@ -68,7 +69,7 @@ public void run() { try { // Wait request... ScheduledRequest request = processor.getSchedulerQueue().waitSubscribeUnsubscribeRequest(); - logger.debug(">> "+request); + logger.trace(">> "+request); // Process request Response response = null; @@ -77,7 +78,7 @@ public void run() { else if (request.isUnsubscribeRequest()) response = unsubscribe(((InternalUnsubscribeRequest) request.getRequest()).getSpuid()); - logger.debug("<< "+response); + logger.trace("<< "+response); // Send back response processor.getSchedulerQueue().addResponse(request.getToken(), response); @@ -89,7 +90,7 @@ else if (request.isUnsubscribeRequest()) } } - private Response subscribe(InternalSubscribeRequest req) { + private synchronized Response subscribe(InternalSubscribeRequest req) { SubscribeProcessorBeans.subscribeRequest(); EventHandler eventHandler = req.getEventHandler(); @@ -114,100 +115,106 @@ private Response subscribe(InternalSubscribeRequest req) { return new SubscribeResponse(spuid, req.getAlias(), spu.getLastBindings()); } - private Response unsubscribe(String spuid) { + private synchronized Response unsubscribe(String spuid) { SubscribeProcessorBeans.unsubscribeRequest(); - synchronized (activeSpus) { + //synchronized (activeSpus) { String masterSpuid = spuids.get(spuid); - logger.debug("Master spuid: " + masterSpuid + " (" + spuid + ")"); + logger.trace("@unsubscribe spuid: " + spuid); - if (masterSpuid == null) + if (masterSpuid == null) { + logger.error("SPUID not found: " + spuid); return new ErrorResponse(404, "spuid_not_found", "SPUID not found: " + spuid); + } // Unregister handler unregisterHandler(masterSpuid, spuid); - } + //} return new UnsubscribeResponse(spuid); } - public void killSpu(String spuid) { - synchronized (activeSpus) { - unregisterHandler(spuids.get(spuid), spuid); - } - } +// public void killSpu(String spuid) { +// synchronized (activeSpus) { +// unregisterHandler(spuids.get(spuid), spuid); +// } +// } @Override - public void notifyEvent(Notification notify) { + public synchronized void notifyEvent(Notification notify) { // synchronized (handlers) { logger.debug("@notifyEvent: " + notify); String spuid = notify.getSpuid(); - ArrayList toBeKilled = new ArrayList(); +// ArrayList toBeKilled = new ArrayList(); - synchronized (activeSpus) { + //synchronized (activeSpus) { if (activeSpus.containsKey(spuid)) { for (String client : activeSpus.get(spuid)) { try { // Dispatching events Notification event = new Notification(client, notify.getARBindingsResults(), sequenceNumbers.get(client)); - handlers.get(client).notifyEvent(event); + subscribers.get(client).notifyEvent(event); sequenceNumbers.put(client, sequenceNumbers.get(client) + 1); } catch (Exception e) { - logger.error("@notifyEvent Client:" + client + " Notification: " + notify + " Exception:" - + e.getMessage()); + logger.error(e.getMessage()); - // Handler is gone: unregister it - toBeKilled.add(client); +// // Handler is gone: unregister it +// toBeKilled.add(client); } } - for (String client : toBeKilled) - unregisterHandler(spuid, client); +// for (String client : toBeKilled) +// unregisterHandler(spuid, client); } - } + //} } - private void registerHandler(String masterSpuid, String spuid, EventHandler handler) { - logger.debug("Register SPU handler: " + spuid); - - SubscribeProcessorBeans.registerHandler(); + private synchronized void registerHandler(String masterSpuid, String spuid, EventHandler handler) { + logger.debug("SPU: "+masterSpuid + " register handler: " + spuid); - synchronized (activeSpus) { + //synchronized (activeSpus) { + // Register subscriber + subscribers.put(spuid, handler); + sequenceNumbers.put(spuid, 1); + spuids.put(spuid, masterSpuid); + + SubscribeProcessorBeans.addSubscriber(); + + // Link SPUID to active SPU if (activeSpus.get(masterSpuid) == null) activeSpus.put(masterSpuid, new ArrayList()); activeSpus.get(masterSpuid).add(spuid); - - handlers.put(spuid, handler); - sequenceNumbers.put(spuid, 1); - spuids.put(spuid, masterSpuid); - } + //} } - private void unregisterHandler(String masterSpuid, String spuid) { - logger.debug("Unregister SPU handler: " + spuid); - - SubscribeProcessorBeans.unregisterHandler(); + private synchronized void unregisterHandler(String masterSpuid, String spuid) { + logger.debug("Unregister handler: " + spuid + " SPUID: "+masterSpuid); // SPUids - synchronized (activeSpus) { + //synchronized (activeSpus) { + // Unregister subscriber spuids.remove(spuid); sequenceNumbers.remove(spuid); - handlers.remove(spuid); + subscribers.remove(spuid); + + SubscribeProcessorBeans.removeSubscriber(); + // Subscriber not yet activated ==> return if (!activeSpus.containsKey(masterSpuid)) return; activeSpus.get(masterSpuid).remove(spuid); - logger.debug(masterSpuid + " number of clients: " + activeSpus.get(masterSpuid).size()); + if (activeSpus.get(masterSpuid).isEmpty()) { activeSpus.remove(masterSpuid); // Deactivate SPU + logger.debug("Deactivate SPU: "+masterSpuid); processor.getSPUManager().deactivate(masterSpuid); } - } + //} } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java index 30168d34..a90b0771 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java @@ -13,7 +13,7 @@ import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.timing.Timings; -public class SPARQL11ResponseHandler implements ResponseHandler { +public class SPARQL11ResponseHandler extends ResponseHandler { protected final Logger logger = LogManager.getLogger(); private HttpAsyncExchange handler; @@ -38,5 +38,4 @@ public void sendResponse(Response response) { jmx.stop(handler); logger.trace(response); } - } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index ec7f36d3..0155e8b9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -20,7 +20,6 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; -import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; @@ -34,9 +33,9 @@ protected String getWelcomeMessage() { return "SPARQL 1.1 Subscribe | wss://%s:%d%s"; } - public SecureWebsocketServer(int port, String path, Scheduler scheduler, AuthorizationManager oauth, DependabilityManager dependabilityMng) + public SecureWebsocketServer(int port, String path, Scheduler scheduler, AuthorizationManager oauth) throws SEPAProtocolException, SEPASecurityException { - super(port, path, scheduler, dependabilityMng); + super(port, path, scheduler); if (oauth == null) throw new IllegalArgumentException("Authorization manager is null"); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java index a8d4b055..9a71ef82 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java @@ -6,27 +6,19 @@ import org.java_websocket.exceptions.WebsocketNotConnectedException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.core.EventHandler; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; -public class WebsocketEventHandler implements EventHandler ,ResponseHandler { +public class WebsocketEventHandler extends ResponseHandler implements EventHandler { private static final Logger logger = LogManager.getLogger(); private final WebSocket socket; - // Dependability manager - private final DependabilityManager dependabilityMng; - - public WebsocketEventHandler(WebSocket s,DependabilityManager dependabilityMng){ + public WebsocketEventHandler(WebSocket s){ this.socket = s; - this.dependabilityMng = dependabilityMng; } private void send(Response ret) throws SEPAProtocolException { @@ -42,20 +34,7 @@ private void send(Response ret) throws SEPAProtocolException { @Override public void sendResponse(Response response) throws SEPAProtocolException { logger.trace(response); - - if (response.isSubscribeResponse()) { - WebsocketBeans.subscribeResponse(); - dependabilityMng.onSubscribe(socket.hashCode(), ((SubscribeResponse)response).getSpuid()); - } - else if (response.isUnsubscribeResponse()) { - WebsocketBeans.unsubscribeResponse(); - dependabilityMng.onUnsubscribe(socket.hashCode(), ((UnsubscribeResponse)response).getSpuid()); - } - else if (response.isError()) { - WebsocketBeans.errorResponse(); - logger.error(response); - dependabilityMng.onError(socket.hashCode(), (ErrorResponse)response); - } + send(response); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index bf1c2c47..a4529d66 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -6,6 +6,7 @@ import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.HashMap; +import java.util.UUID; import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; @@ -26,7 +27,6 @@ import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; -import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; @@ -53,18 +53,20 @@ protected String getWelcomeMessage() { // Active sockets protected final HashMap activeSockets = new HashMap(); + // Sockets UUIDs + protected final HashMap uuids = new HashMap(); + // Dependability manager - private final DependabilityManager dependabilityMng; + //private final DependabilityManager dependabilityMng; - public WebsocketServer(int port, String path, Scheduler scheduler, DependabilityManager dependabilityMng) + public WebsocketServer(int port, String path, Scheduler scheduler) throws SEPAProtocolException { super(new InetSocketAddress(port)); - if (path == null || scheduler == null || dependabilityMng == null) + if (path == null || scheduler == null) throw new SEPAProtocolException(new IllegalArgumentException("One or more arguments are null")); this.scheduler = scheduler; - this.dependabilityMng = dependabilityMng; this.path = path; SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); @@ -87,13 +89,23 @@ public void onOpen(WebSocket conn, ClientHandshake handshake) { if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND,"wrong_path", + ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND, "wrong_path", "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); conn.send(response.toString()); return; } fragmentedMessages.put(conn, null); + + // Add active socket + if (!activeSockets.containsKey(conn)) { + WebsocketEventHandler handler = new WebsocketEventHandler(conn); + + uuids.put(conn, handler.getUUID()); + activeSockets.put(conn,handler); + + logger.debug("Handler UUID: "+handler.getUUID()+" Total handlers: "+activeSockets.size()); + } } @Override @@ -106,7 +118,7 @@ public void onClose(WebSocket conn, int code, String reason, boolean remote) { fragmentedMessages.remove(conn); // KILL ALL SPUs - dependabilityMng.onBrokenSocket(conn.hashCode()); + scheduler.onBrokenSubscription(uuids.get(conn)); // Remove active socket activeSockets.remove(conn); @@ -120,17 +132,12 @@ public void onMessage(WebSocket conn, String message) { if (!conn.getResourceDescriptor().equals(path)) { logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND,"wrong_path", + ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND, "wrong_path", "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); conn.send(response.toString()); return; } - // Add active socket - if (!activeSockets.containsKey(conn)) { - activeSockets.put(conn, new WebsocketEventHandler(conn, dependabilityMng)); - } - // Parse the request InternalRequest req = parseRequest(message, conn); if (req == null) { @@ -145,8 +152,7 @@ public void onMessage(WebSocket conn, String message) { ScheduledRequest request = scheduler.schedule(req, activeSockets.get(conn)); if (request == null) { logger.error("Out of tokens"); - ErrorResponse response = new ErrorResponse(429,"too_many_requests", - "Too many pending requests"); + ErrorResponse response = new ErrorResponse(429, "too_many_requests", "Too many pending requests"); conn.send(response.toString()); } } @@ -177,7 +183,8 @@ protected InternalRequest parseRequest(String request, WebSocket conn) try { req = new JsonParser().parse(request).getAsJsonObject(); } catch (JsonParseException e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException","JsonParseException: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException", + "JsonParseException: " + request); conn.send(error.toString()); logger.error(error); return null; @@ -192,7 +199,8 @@ protected InternalRequest parseRequest(String request, WebSocket conn) try { sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); } catch (Exception e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","sparql member not found: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception", + "sparql member not found: " + request); conn.send(error.toString()); logger.error(error); return null; @@ -213,13 +221,13 @@ protected InternalRequest parseRequest(String request, WebSocket conn) } catch (Exception e) { } - return new InternalSubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri,activeSockets.get(conn)); + return new InternalSubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri, activeSockets.get(conn)); } else if (req.has("unsubscribe")) { String spuid; try { spuid = req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString(); } catch (Exception e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","spuid member not found: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception", "spuid member not found: " + request); conn.send(error.toString()); return null; } @@ -227,7 +235,7 @@ protected InternalRequest parseRequest(String request, WebSocket conn) return new InternalUnsubscribeRequest(spuid); } - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unsupported","Bad request: " + request); + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unsupported", "Bad request: " + request); conn.send(error.toString()); return null; } @@ -320,7 +328,7 @@ public long getSubscribeResponse() { public long getUnsubscribeResponse() { return WebsocketBeans.getUnsubscribeResponses(); } - + @Override public long getNotifications() { return WebsocketBeans.getNotifications(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 01534de4..91cb153b 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -19,18 +19,22 @@ package it.unibo.arces.wot.sepa.engine.scheduling; import java.util.HashMap; +import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; - +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; +import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.SchedulerBeans; - +import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; +import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; import it.unibo.arces.wot.sepa.engine.timing.Timings; /** @@ -41,82 +45,119 @@ public class Scheduler extends Thread implements SchedulerMBean { private static final Logger logger = LogManager.getLogger(); private final AtomicBoolean running = new AtomicBoolean(true); - + // Responders private HashMap responders = new HashMap(); + private HashMap handlers = new HashMap(); // Synchronized queues private final SchedulerQueue queue; - + + private final DependabilityManager dependability; + public Scheduler(EngineProperties properties) { if (properties == null) { logger.error("Properties are null"); throw new IllegalArgumentException("Properties are null"); } - + queue = new SchedulerQueue(properties.getSchedulingQueueSize()); + // Dependability manager + dependability = new DependabilityManager(queue); + // JMX SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SchedulerBeans.setQueueSize(properties.getSchedulingQueueSize()); SchedulerBeans.setTimeout(properties.getSchedulerTimeout()); - + setName("SEPA-Scheduler"); } - + public synchronized ScheduledRequest schedule(InternalRequest request, ResponseHandler handler) { - if (request == null || handler == null) { - logger.error("Request handler or request are null"); - return null; - } - - // Add request to the scheduler queue (null means no more tokens) - ScheduledRequest scheduled = queue.addRequest(request, handler); - - // No more tokens - if (scheduled == null) { - SchedulerBeans.newRequest(request, false); - logger.error("Request refused: too many pending requests: "+request); - return null; + synchronized (responders) { + if (request == null || handler == null) { + logger.error("Request handler or request are null"); + return null; + } + + // Add request to the scheduler queue (null means no more tokens) + ScheduledRequest scheduled = queue.addRequest(request, handler); + + // No more tokens + if (scheduled == null) { + SchedulerBeans.newRequest(request, false); + logger.error("Request refused: too many pending requests: " + request); + return null; + } + + logger.info(">> " + scheduled); + + Timings.log(request); + + SchedulerBeans.newRequest(request, true); + + // Register response handlers + responders.put(scheduled.getToken(), handler); + handlers.put(scheduled.getToken(), handler.getUUID()); + + return scheduled; } - - logger.info(">> "+scheduled); - - // Register response handler - responders.put(scheduled.getToken(), handler); - - Timings.log(request); - - SchedulerBeans.newRequest(request, true); - - return scheduled; } @Override public void run() { - while(running.get()) { + while (running.get()) { try { // Wait for response ScheduledResponse response = queue.waitResponse(); - logger.info("<< "+response); - + logger.info("<< " + response); + // The token int token = response.getToken(); - - // Send response back and remove handler - if (responders.get(token) != null) - try { - responders.get(token).sendResponse(response.getResponse()); - } catch (SEPAProtocolException e) { - logger.error("Failed to send response: "+e.getMessage()); + + synchronized (responders) { + // Send response back + ResponseHandler handler = responders.get(token); + if (handler == null) { + logger.warn("Response handler is null (token #" + token + ")"); + } else { + try { + handler.sendResponse(response.getResponse()); + } catch (SEPAProtocolException e) { + logger.error("Failed to send response: " + e.getMessage()); + } } - responders.remove(token); + + // Dependability + if (response.getResponse().isSubscribeResponse()) { + WebsocketBeans.subscribeResponse(); + dependability.onSubscribe(handlers.get(token), + ((SubscribeResponse) response.getResponse()).getSpuid()); + } else if (response.getResponse().isUnsubscribeResponse()) { + WebsocketBeans.unsubscribeResponse(); + dependability.onUnsubscribe(handlers.get(token), + ((UnsubscribeResponse) response.getResponse()).getSpuid()); + } else if (response.getResponse().isError()) { + WebsocketBeans.errorResponse(); + logger.error(response); + dependability.onError(handlers.get(token), (ErrorResponse) response.getResponse()); + } + + // Remove handlers + responders.remove(token); + handlers.remove(token); + } } catch (InterruptedException e) { running.set(false); } } } - + + public void onBrokenSubscription(UUID uuid) { + dependability.onBrokenSubscription(uuid); + } + public void finish() { running.set(false); } From 985d5b237346632c6b93a52e13455313ff9cdafb Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 13 Sep 2018 09:05:31 +0200 Subject: [PATCH 46/76] Update .travis.yml Fixed path for tests (secure and not secure) --- .travis.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7cee58dc..ea74ddf0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,26 +13,30 @@ jobs: - sleep 30 - wget http://localhost:9999/blazegraph/namespace/kb/sparql - mvn package - - cd engine/target + - cd engine/src/test/resources - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar - - java -jar engine-0-SNAPSHOT.jar > log.txt & + - java -jar engine-0-SNAPSHOT.jar -engine=engine.jpar > log.txt & - sleep 30 - - cd ../.. + - cd ../../../.. script: + - cd client-api/src/test/resources - mvn verify -D testConfiguration=sepatest.jsap + - cd ../../../.. - stage: integrationSecure before_script: - docker run --name blazegraph -d -p 9999:9999 nawer/blazegraph:2.1.1 - sleep 30 - wget http://localhost:9999/blazegraph/namespace/kb/sparql - mvn package - - cd engine/target + - cd engine/src/test/resources - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar - - java -jar engine-0-SNAPSHOT.jar -secure > log.txt & + - java -jar engine-0-SNAPSHOT.jar -engine=engine-secure.jpar > log.txt & - sleep 30 - - cd ../.. + - cd ../../../.. script: + - cd client-api/src/test/resources - mvn verify -D testConfiguration=sepatest-secure.jsap + - cd ../../../.. - stage: deploy env: - secure: UBX6IAsuQFTWSlR5n/75KLa1bPFBFNg2K50d59mgrTPSfPt9zWyW04qCrirR8MGCO6SRBs1PZTR4odCwn3gMRNU388IVBauNiBw3nFkHUWeW61GXFIbZcUCwmzZasfhFlqtq9BAb+jhcsgM1EQQtz9Y6ZxgyA0O3bCEv2oJsCbdR74qGR1rfpLhGy8jUMzMWGEZ/MWyRM/HyQYXiUiQBcAdqwZVImc7k9+sZmo5gSchI0bhl/DZlzE/PvVGMh+asypYsSpOIF7HijLNVPy7pzXRWmnA4aTX2s93YSZaaB+Ux1VUPClGfzk+ZaopdzqC7L00+cBBd7r6vEUftbAoL/nIvti0TvL2onhtIGPeA5pozroUxAOgH4/ImndVm/ZZoawp2psix4wMC8yVg94hxBfZN/HU96Rh6ckHY4u5faLE7yEBmTEu9G4nz3pZnY8bj+sJBwXq9J1UB0FjTCnivZTAFQWMIcG1UBNhrnvrh/mBhnLr4sXnK5x1Y9ufzJlcVIWmnWUcEjWRRc26r9AVAxrPMRx0t2bY+Hqr/ggM2+zlKMIf/nYI5JtDbseXdldIJeGT4bVMq2q9I0TF9Kg4oSznyn5dO/IfN1wb2M67ZJPpHpQL1wX0HsLgj/5z09wBo7DLO4A24c5+iP32SuRORGmzJxeAsAnw+OKK7Y3IHgHU= From 3a6f97e5dc2c13f0a4ccc0853d4a69a0aa4e46f0 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 13 Sep 2018 09:16:55 +0200 Subject: [PATCH 47/76] Update .travis.yml Using engine/target and client-api/target as directories --- .travis.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea74ddf0..bf69e492 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,30 +13,30 @@ jobs: - sleep 30 - wget http://localhost:9999/blazegraph/namespace/kb/sparql - mvn package - - cd engine/src/test/resources + - cd engine/target - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar - java -jar engine-0-SNAPSHOT.jar -engine=engine.jpar > log.txt & - sleep 30 - - cd ../../../.. + - cd ../.. script: - - cd client-api/src/test/resources + - cd client-api/target - mvn verify -D testConfiguration=sepatest.jsap - - cd ../../../.. + - cd ../.. - stage: integrationSecure before_script: - docker run --name blazegraph -d -p 9999:9999 nawer/blazegraph:2.1.1 - sleep 30 - wget http://localhost:9999/blazegraph/namespace/kb/sparql - mvn package - - cd engine/src/test/resources + - cd engine/target - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar - java -jar engine-0-SNAPSHOT.jar -engine=engine-secure.jpar > log.txt & - sleep 30 - - cd ../../../.. + - cd ../.. script: - - cd client-api/src/test/resources + - cd client-api/target - mvn verify -D testConfiguration=sepatest-secure.jsap - - cd ../../../.. + - cd ../.. - stage: deploy env: - secure: UBX6IAsuQFTWSlR5n/75KLa1bPFBFNg2K50d59mgrTPSfPt9zWyW04qCrirR8MGCO6SRBs1PZTR4odCwn3gMRNU388IVBauNiBw3nFkHUWeW61GXFIbZcUCwmzZasfhFlqtq9BAb+jhcsgM1EQQtz9Y6ZxgyA0O3bCEv2oJsCbdR74qGR1rfpLhGy8jUMzMWGEZ/MWyRM/HyQYXiUiQBcAdqwZVImc7k9+sZmo5gSchI0bhl/DZlzE/PvVGMh+asypYsSpOIF7HijLNVPy7pzXRWmnA4aTX2s93YSZaaB+Ux1VUPClGfzk+ZaopdzqC7L00+cBBd7r6vEUftbAoL/nIvti0TvL2onhtIGPeA5pozroUxAOgH4/ImndVm/ZZoawp2psix4wMC8yVg94hxBfZN/HU96Rh6ckHY4u5faLE7yEBmTEu9G4nz3pZnY8bj+sJBwXq9J1UB0FjTCnivZTAFQWMIcG1UBNhrnvrh/mBhnLr4sXnK5x1Y9ufzJlcVIWmnWUcEjWRRc26r9AVAxrPMRx0t2bY+Hqr/ggM2+zlKMIf/nYI5JtDbseXdldIJeGT4bVMq2q9I0TF9Kg4oSznyn5dO/IfN1wb2M67ZJPpHpQL1wX0HsLgj/5z09wBo7DLO4A24c5+iP32SuRORGmzJxeAsAnw+OKK7Y3IHgHU= From 3130c1c42a17f50aa63a06cb7890aa69332ecd1d Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Thu, 13 Sep 2018 09:23:33 +0200 Subject: [PATCH 48/76] Files for Travis --- engine/jmx.properties | 67 --------------------------- engine/jmxremote.access | 4 -- engine/jmxremote.password | 3 -- engine/src/test/resources/log4j2.xml | 33 +++++++++++++ engine/src/test/resources/sepa.jks | Bin 0 -> 2317 bytes 5 files changed, 33 insertions(+), 74 deletions(-) delete mode 100644 engine/jmx.properties delete mode 100644 engine/jmxremote.access delete mode 100644 engine/jmxremote.password create mode 100644 engine/src/test/resources/log4j2.xml create mode 100644 engine/src/test/resources/sepa.jks diff --git a/engine/jmx.properties b/engine/jmx.properties deleted file mode 100644 index c4b9b7e4..00000000 --- a/engine/jmx.properties +++ /dev/null @@ -1,67 +0,0 @@ -# Please refer to: -# http://docs.oracle.com/javase/7/docs/technotes/guides/management/agent.html -# -# Enables the JMX remote agent and local monitoring via a JMX connector published on a private interface -# used by JConsole and any other local JMX clients that use the Attach API. -# JConsole can use this connector if it is started by the same user as the user that started the agent. -# No password or access files are checked for requests coming via this connector. -# true / false. Default is true. -com.sun.management.jmxremote=true - -# Enables the JMX remote agent and creates a remote JMX connector to listen through the specified port. -# By default, the SSL, password, and access file properties are used for this connector. -# It also enables local monitoring as described for the com.sun.management.jmxremote property. -# Port number. No default. -com.sun.management.jmxremote.port=5555 - -#Binds the RMI connector stub to an RMI registry protected by SSL. -#true / false. Default is false. -com.sun.management.jmxremote.registry.ssl=false - -#Enables secure monitoring via SSL. If false, then SSL is not used. -#true / false. Default is true. -com.sun.management.jmxremote.ssl=false - -#A comma-delimited list of SSL/TLS protocol versions to enable. Used in conjunction with com.sun.management.jmxremote.ssl. -#Default SSL/TLS protocol version. -#com.sun.management.jmxremote.ssl.enabled.protocols - -#A comma-delimited list of SSL/TLS cipher suites to enable. Used in conjunction with com.sun.management.jmxremote.ssl. -#Default SSL/TLS cipher suites. -#com.sun.management.jmxremote.ssl.enabled.cipher.suites - -#If this property is true and the property com.sun.management.jmxremote.ssl is also true, then client authentication -#will be performed. -#It is recommended that you set this property to true. -#true / false. Default is false. -com.sun.management.jmxremote.ssl.need.client.auth=false - -#If this property is false then JMX does not use passwords or access files: all users are allowed all access. -#true / false. Default is true. -com.sun.management.jmxremote.authenticate=true - -#Specifies location for password file. If com.sun.management.jmxremote.authenticate is false, then this property -# and the password and access files are ignored. -#Otherwise, the password file must exist and be in the valid format. If the password file is empty or nonexistent, -#then no access is allowed. -#JRE_HOME/lib/management/jmxremote.password -com.sun.management.jmxremote.password.file=./jmxremote.password - -#Specifies location for the access file. If com.sun.management.jmxremote.authenticate is false, -#then this property and the password and access files are ignored. -#Otherwise, the access file must exist and be in the valid format. If the access file is empty or nonexistent, -#then no access is allowed. -#JRE_HOME/lib/management/ jmxremote.access -com.sun.management.jmxremote.access.file=./jmxremote.access - -#Specifies the name of a Java Authentication and Authorization Service (JAAS) login configuration entry to use when -#the JMX agent authenticates users. -#When using this property to override the default login configuration, the named configuration entry must be in a file -#that is loaded by JAAS. -#In addition, the login modules specified in the configuration should use the name and password callbacks to acquire -#the user's credentials. -#For more information, see the API documentation for javax.security.auth.callback.NameCallback and -#javax.security.auth.callback.PasswordCallback. -#Default login configuration is a file-based password authentication. -#com.sun.management.jmxremote.login.config - diff --git a/engine/jmxremote.access b/engine/jmxremote.access deleted file mode 100644 index 95e3a480..00000000 --- a/engine/jmxremote.access +++ /dev/null @@ -1,4 +0,0 @@ -# The "monitorRole" role has readonly access. -# The "controlRole" role has readwrite access. -guest readonly -root readwrite \ No newline at end of file diff --git a/engine/jmxremote.password b/engine/jmxremote.password deleted file mode 100644 index 71bda97b..00000000 --- a/engine/jmxremote.password +++ /dev/null @@ -1,3 +0,0 @@ -#chmod 600 jmxremote.password -guest guest -root root \ No newline at end of file diff --git a/engine/src/test/resources/log4j2.xml b/engine/src/test/resources/log4j2.xml new file mode 100644 index 00000000..639d3c4c --- /dev/null +++ b/engine/src/test/resources/log4j2.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/engine/src/test/resources/sepa.jks b/engine/src/test/resources/sepa.jks new file mode 100644 index 0000000000000000000000000000000000000000..4b4d14d0ad13840ce7960e7ab898fe46f439e0a8 GIT binary patch literal 2317 zcmd6o={wX58^>q0%-Gkc$&g+64bj9wwlpF8n&n{ZYi0;%TFlsGElt)ajU|b)B}&=F zi0n(Yk;qyiOB|i&d9LgH3D1lB#pk}iFYfDmeXsjISUFe$fk2R>0{>IY1ot2pU-$4M zb|`om_8kO*LjW@D0N`Lk$T5LoU|9$^7z_u&$grjPQpWeNp)+?%B7Tk08!AJ^+Hq_nwwu41kD{bxSZf**5y zn>%ua<_UeH{llWfw7Q!$6^E+5QQDRLC&mT7v$%A(I-1 zLQ-3yZkI9-6U|;Mq!JuIjOHztxX(Qz&GbGrohh(0-HY?#(lNll#b+d#4SuVpwMyvh zmRn=DoEsdry$x&L3QH%wEkGBoB-it696AWclkIsx{(HRHsiI*eNV^G!l@G}AH-Tu zex=n!$LH&xN7Z0?_C_r4P*tqLEEjB^raCzd( zPBOIBrCDp{IftlHXE$5qm(HAar;>$$Y+~IId#I>5$|t|vO+_bkWK(1?fV2F9IKNuz z=l<$}fs11)x|)-pSizvfbB0@75cx;RA?;bTCz)mN{T(i1EF$ba97x@S+0-`5Q(|Pk zY6Y3Nb>uu&clwyBxa^UA<0Anjs(BN?_F8wZAXZ_fU%es~mVaE?YqvDf49V12Tz5k! zr|S90_!284`qTQmgIk;|_d*#y{Q7I}Q^p_Mg{!CAqiKm>{?L^hy1Ge;l#Y&y z-13^FH|nC44s08Vj~OG`3IHP776q%U3ob5`VH328KmWK;VNh3%b!)*OG$(%yGp-eV z(JW@7@hJqI#cYM#!zt+%TJRs|h-w)FB2 z@Fbwk-3jh4!ERm{c|h*B0Lm+FVXAFzW`x!cbq^q-O@jm7+zEsuEn53X=1p{WBZdUK z11AO9F=qi)j50=91*5ELe}q^-6~O)%{|`4NgT?-B>(SSNlEIuHfDA@J$Y3yNO`hbD zJjrK}-o5}Ej? z?;MlMy_$OAU<>C^LlQbQ_xvl#DO`krY*t@?*-7199v^|a@YQNBO`F&l<*KRU7b;CQ zgoZ*W5|o;-@4>e1n|i1j-MWevNwi{N(s@mh7n*U2Z9*qbC;Z%Zo>IuO3dG+__D6<{ z>W^Pidoa46Bia~k`Lf%QEV8;0{uFzSLhZ+y-B<&tgHHw8EYq8_p3awc)=Qe!_y3@s zrk5$ypK-Wx`z+_u``fi%HiMz60#Mu0NgZtDc!fCw0YB|uq`0=1>g$4~K%ig{_>m~^ zpQNBTU;;3{q5dueKggIB4~YoV7!%@~pIIONw~0sDJTegslB6vI!#?!&xNmy!H153S z>ynAdlEbpOvs9S=%5tqZ?8R`;T;0cs@rW-Ijjyqmq$+imsU7TW+oAyfEh2EtdQWO2 ztjeG9Q0nsH$)=U29oxuz?2jaHbD3?G_|Wwm3mpr`d@JI5X2Y*gt$LK4Do6N7BF=RS zPc3iv$jzO;4qEg3$?O?ut3EYJPqU&is)~?pPQ!I%*xieoA;*Xz_{p}FB=ak)L!N2- zi7NvcP1CV!#*el2y<5JuV`h`H@VIs!CB=zT(SmYPQbC3g89Hg=La&mLkfH=>Oi1ck zHr8}+igV5C*!78FGZm+el_AD7-4dbFa80vAG-Z>%S!3j+Qx Date: Thu, 13 Sep 2018 15:55:28 +0200 Subject: [PATCH 49/76] Add virtuoso stage in travis automated testing A simple test to check if virtuoso could be used in docker. --- .travis.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6149ffaa..2da5d6aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,19 @@ jobs: - cd ../.. script: - mvn verify + - stage: integration-virtuoso + before_script: + - docker run --name my-virtuoso -p 8890:8890 -p 1111:1111 -e DBA_PASSWORD=myDbaPassword -e SPARQL_UPDATE=true -e DEFAULT_GRAPH=http://www.example.com/my-graph -d tenforce/virtuoso + - sleep 30 + - wget http://localhost:9999/blazegraph/namespace/kb/sparql + - mvn package + - cd engine/target + - mv endpoints/virtuoso.jpar endpoint.jpar + - java -jar engine-0-SNAPSHOT.jar > log.txt & + - sleep 30 + - cd ../.. + script: + - mvn verify - stage: deploy env: - secure: UBX6IAsuQFTWSlR5n/75KLa1bPFBFNg2K50d59mgrTPSfPt9zWyW04qCrirR8MGCO6SRBs1PZTR4odCwn3gMRNU388IVBauNiBw3nFkHUWeW61GXFIbZcUCwmzZasfhFlqtq9BAb+jhcsgM1EQQtz9Y6ZxgyA0O3bCEv2oJsCbdR74qGR1rfpLhGy8jUMzMWGEZ/MWyRM/HyQYXiUiQBcAdqwZVImc7k9+sZmo5gSchI0bhl/DZlzE/PvVGMh+asypYsSpOIF7HijLNVPy7pzXRWmnA4aTX2s93YSZaaB+Ux1VUPClGfzk+ZaopdzqC7L00+cBBd7r6vEUftbAoL/nIvti0TvL2onhtIGPeA5pozroUxAOgH4/ImndVm/ZZoawp2psix4wMC8yVg94hxBfZN/HU96Rh6ckHY4u5faLE7yEBmTEu9G4nz3pZnY8bj+sJBwXq9J1UB0FjTCnivZTAFQWMIcG1UBNhrnvrh/mBhnLr4sXnK5x1Y9ufzJlcVIWmnWUcEjWRRc26r9AVAxrPMRx0t2bY+Hqr/ggM2+zlKMIf/nYI5JtDbseXdldIJeGT4bVMq2q9I0TF9Kg4oSznyn5dO/IfN1wb2M67ZJPpHpQL1wX0HsLgj/5z09wBo7DLO4A24c5+iP32SuRORGmzJxeAsAnw+OKK7Y3IHgHU= From ae6b4f8da143c56ca944294b33c8ddc33b5348f4 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 13 Sep 2018 15:58:11 +0200 Subject: [PATCH 50/76] Revert "Add Added and Removed processing" This reverts commit 14bb680. This is obsolete changes that will be overrided with the merge of the new branch --- engine/pom.xml | 6 +- .../engine/processing/SPARQLAnalyzer.java | 265 ------------------ .../engine/processing/UpdateConstruct.java | 34 --- .../engine/processing/UpdateProcessor.java | 54 +--- .../processing/UpdateResponseWithAR.java | 30 -- .../java/engine/ConfigurationProvider.java | 23 -- .../test/java/engine/ITUpdateProcessor.java | 64 ----- .../engine/processing/SPARQLAnalyzerTest.java | 55 ---- .../SPUMangerTest.java | 4 +- engine/src/test/resources/dev.jsap | 46 --- 10 files changed, 8 insertions(+), 573 deletions(-) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java delete mode 100644 engine/src/test/java/engine/ConfigurationProvider.java delete mode 100644 engine/src/test/java/engine/ITUpdateProcessor.java delete mode 100644 engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java rename engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/{subscriptions => subscription}/SPUMangerTest.java (89%) delete mode 100644 engine/src/test/resources/dev.jsap diff --git a/engine/pom.xml b/engine/pom.xml index d92c311b..beaba1a8 100644 --- a/engine/pom.xml +++ b/engine/pom.xml @@ -164,11 +164,7 @@ client-api ${revision} - - org.apache.jena - jena-arq - 3.4.0 - + junit junit diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java deleted file mode 100644 index 2e8dfa2c..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java +++ /dev/null @@ -1,265 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import org.apache.jena.atlas.lib.Sink; -import org.apache.jena.graph.Triple; -import org.apache.jena.query.Query; -import org.apache.jena.query.QueryFactory; -import org.apache.jena.query.Syntax; -import org.apache.jena.sparql.algebra.*; -import org.apache.jena.sparql.algebra.op.OpBGP; -import org.apache.jena.sparql.core.BasicPattern; -import org.apache.jena.sparql.core.Quad; -import org.apache.jena.sparql.core.TriplePath; -import org.apache.jena.sparql.lang.UpdateParserFactory; -import org.apache.jena.sparql.lang.arq.ParseException; -import org.apache.jena.sparql.modify.request.*; -import org.apache.jena.sparql.syntax.*; -import org.apache.jena.update.Update; -import org.apache.jena.update.UpdateFactory; -import org.apache.jena.update.UpdateRequest; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -public class SPARQLAnalyzer { - - String test = null; - - public void setString(String s) { - test = s; - } - - class MyTransform extends TransformCopy - { - @Override - public Op transform(OpBGP opBGP) - { - // create a new construct query - Query q = QueryFactory.make(); - q.setQueryConstructType(); - - // parse the bgp - BasicPattern b = opBGP.getPattern(); - Iterator opIterator = b.iterator(); - Template ttt = new Template(b); - q.setConstructTemplate(ttt); - ElementGroup body = new ElementGroup(); - ElementUnion union = new ElementUnion(); - - while (opIterator.hasNext()){ - Triple bb = opIterator.next(); - - // for the query - ElementTriplesBlock block = new ElementTriplesBlock(); // Make a BGP - block.addTriple(bb); - body.addElement(block); - logger.debug(bb.toString()); - - // union - union.addElement(block); - - } - - q.setQueryPattern(body); - q.setQueryPattern(union); - - setString(q.toString()); - logger.debug(q.toString()); - - return opBGP; - } - } - - class ToConstructUpdateVisitor extends UpdateVisitorBase{ - private UpdateConstruct result = new UpdateConstruct("",""); - @Override - public void visit(UpdateDataInsert updateDataInsert) { - Query insertQuery = createBaseConstruct(new QuadAcc(updateDataInsert.getQuads())); - String insertString = insertQuery.isUnknownType() ? "" : insertQuery.serialize() + "WHERE{}"; - result = new UpdateConstruct("",insertString); - } - - - - @Override - public void visit(UpdateDataDelete updateDataDelete) { - Query deleteQuery = createBaseConstruct(new QuadAcc(updateDataDelete.getQuads())); - String deleteString = deleteQuery.isUnknownType() ? "" : deleteQuery.serialize()+"WHERE{}"; - result = new UpdateConstruct(deleteString,""); - } - - @Override - public void visit(UpdateDeleteWhere updateDeleteWhere) { - Query updateDeleteQuery = createBaseConstruct(new QuadAcc(updateDeleteWhere.getQuads())); - if(!updateDeleteQuery.isUnknownType()) { - ElementGroup where = new ElementGroup(); - for (Quad q : updateDeleteWhere.getQuads()) { - where.addTriplePattern(q.asTriple()); - } - updateDeleteQuery.setQueryPattern(where); - result = new UpdateConstruct(updateDeleteQuery.serialize(), ""); - } - } - - @Override - public void visit(UpdateModify updateModify) { - String insertString = ""; - String deleteString = ""; - - if(updateModify.hasDeleteClause() && !updateModify.getDeleteAcc().getQuads().isEmpty()){ - Template constructDelete = new Template(updateModify.getDeleteAcc()); - Query constructQueryDelete = new Query(); - constructQueryDelete.setQueryConstructType(); - constructQueryDelete.setConstructTemplate(constructDelete); - constructQueryDelete.setQueryPattern(updateModify.getWherePattern()); - deleteString = constructQueryDelete.toString(); - } - - if(updateModify.hasInsertClause() && !updateModify.getInsertAcc().getQuads().isEmpty()){ - Template constructInsert = new Template(updateModify.getInsertAcc()); - Query constructQueryInsert = new Query(); - constructQueryInsert.setQueryConstructType(); - constructQueryInsert.setConstructTemplate(constructInsert); - constructQueryInsert.setQueryPattern(updateModify.getWherePattern()); - insertString = constructQueryInsert.serialize(); - } - - result = new UpdateConstruct(deleteString,insertString); - - } - - @Override - public void visit(UpdateClear update) { - String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct(deleteConstruct,""); - } - - @Override - public void visit(UpdateDrop update) { - String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct(deleteConstruct,""); - } - - @Override - public void visit(UpdateCopy update) { - String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getDest().getGraph().getURI()+"> { ?s ?p ?o } . }"; - String insertConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getSrc().getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct(deleteConstruct,insertConstruct); - } - - @Override - public void visit(UpdateAdd update) { - String insertConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getDest().getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct("",insertConstruct); - } - - //TODO: Move - - public UpdateConstruct getResult() { - return result; - } - - private Query createBaseConstruct( QuadAcc quads) { - Query result = new Query(); - if(!quads.getQuads().isEmpty()){ - Template construct = new Template(quads); - result = new Query(); - result.setQueryConstructType(); - result.setConstructTemplate(construct); - } - return result; - } - - - } - - // attributes - private String sparqlText; - private final static Logger logger = LogManager.getLogger("SPARQLAnalyzer"); - - // Constructor - SPARQLAnalyzer(String request){ - // store the query text - sparqlText = request; - } - - - UpdateConstruct getConstruct() { - UpdateRequest updates = UpdateFactory.create(sparqlText); - for(Update up : updates){ - ToConstructUpdateVisitor updateVisitor = new ToConstructUpdateVisitor(); - up.visit(updateVisitor); - return updateVisitor.getResult(); - } - throw new IllegalArgumentException("No valid operation found"); - } - - // LUTT generator - List getLutt(){ - - // debug print - logger.debug("Analyzing query " + sparqlText); - - // create a variable for the LUTT - List lutt = new ArrayList(); - - // extract basic graph patterns - Query q = QueryFactory.create(sparqlText); - Element e = q.getQueryPattern(); - - // This will walk through all parts of the query - ElementWalker.walk(e, - - // For each element... - new ElementVisitorBase() { - - // ...when it's a block of triples... - public void visit(ElementPathBlock el) { - - // ...go through all the triples... - Iterator triples = el.patternElts(); - while (triples.hasNext()) { - - // get the current triple pattern - TriplePath t = triples.next(); - lutt.add(t); - - // debug print - logger.debug("Found Triple Pattern: " + t.getSubject() + " " + t.getPredicate() + " " + t.getObject()); } - } - } - ); - - // return! - return lutt; - } - - // Construct Generator - String getConstructFromQuery() throws ParseException { - - // This method allows to derive the CONSTRUCT query - // from the SPARQL SUBSCRIPTION - - // get the algebra from the query - - Query qqq = QueryFactory.create(sparqlText, Syntax.syntaxSPARQL); - Op op = Algebra.compile(qqq); - - // get the algebra version of the construct query and - // convert it back to query - Transform transform = new MyTransform() ; - op = Transformer.transform(transform, op) ; - Query q = OpAsQuery.asQuery(op); - // return - return test; - - } - -} \ No newline at end of file diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java deleted file mode 100644 index f3691335..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateConstruct.java +++ /dev/null @@ -1,34 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -public class UpdateConstruct { - - private final String deleteConstruct; - private final String insertConstruct; - - UpdateConstruct(String deleteConstruct, String insertConstruct){ - if(deleteConstruct == null || insertConstruct == null){ - throw new IllegalArgumentException("Construct query cannot be null"); - } - - this.deleteConstruct = deleteConstruct; - this.insertConstruct = insertConstruct; - } - - /** - * Get delete construct string. An empty string indicates that there are no deleted - * triples - * @return a construct sparql query string - */ - public String getInsertConstruct() { - return insertConstruct; - } - - /** - * Get delete construct string. An empty string indicates that there are no deleted - * triples - * @return a construct sparql query string - */ - public String getDeleteConstruct() { - return deleteConstruct; - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java index 49219c58..bcae302f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessor.java @@ -19,18 +19,8 @@ package it.unibo.arces.wot.sepa.engine.processing; import java.time.Instant; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; import java.util.concurrent.Semaphore; -import com.google.gson.JsonObject; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.response.QueryResponse; -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; -import org.apache.jena.update.Update; -import org.apache.jena.update.UpdateFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -63,39 +53,13 @@ public synchronized Response process(UpdateRequest req, int timeout) { } catch (InterruptedException e) { return new ErrorResponse(500,e.getMessage()); } - - // Get the constructs needed to determine added and removed data - - long start = System.currentTimeMillis(); - SPARQLAnalyzer sa = new SPARQLAnalyzer(req.getSPARQL()); - UpdateConstruct constructs = sa.getConstruct(); - - - BindingsResults added = new BindingsResults(new JsonObject()); - BindingsResults removed = new BindingsResults(new JsonObject()); - - String dc = constructs.getDeleteConstruct(); - - if (dc.length() > 0) { - removed = getTriples(timeout, dc); - } - - String ac = constructs.getInsertConstruct(); - if (ac.length() > 0) { - added = getTriples(timeout, ac); - } - - long stop = System.currentTimeMillis(); - logger.debug("* ADDED REMOVED PROCESSING ("+(stop-start)+" ms) *"); - - ProcessorBeans.updateTimings(start, stop); - + // UPDATE the endpoint - start = System.currentTimeMillis(); + long start = System.currentTimeMillis(); Timing.logTiming(req, "ENDPOINT_REQUEST", Instant.now()); Response ret = endpoint.update(req, timeout); Timing.logTiming(req, "ENDPOINT_RESPONSE", Instant.now()); - stop = System.currentTimeMillis(); + long stop = System.currentTimeMillis(); if (endpointSemaphore != null) endpointSemaphore.release(); @@ -103,17 +67,7 @@ public synchronized Response process(UpdateRequest req, int timeout) { logger.debug("* UPDATE PROCESSING ("+(stop-start)+" ms) *"); ProcessorBeans.updateTimings(start, stop); - - ret = ret.isUpdateResponse() ? new UpdateResponseWithAR((UpdateResponse) ret,added,removed) : ret; + return ret; } - - private BindingsResults getTriples(int timeout, String dc) { - BindingsResults removed; - QueryRequest cons1 = new QueryRequest(dc); - logger.debug(cons1.toString()); - removed = ((QueryResponse) endpoint.query(cons1, timeout)).getBindingsResults(); - logger.debug(removed); - return removed; - } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java deleted file mode 100644 index 8073434e..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateResponseWithAR.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; - -/** - * An update response with Added and Removed triples. - */ -public class UpdateResponseWithAR extends UpdateResponse { - - private final BindingsResults added; - private final BindingsResults removed; - - public UpdateResponseWithAR(UpdateResponse ret, BindingsResults added, BindingsResults removed) { - super(ret); - if(added == null || removed == null){ - throw new IllegalArgumentException("Bindings cannot be null"); - } - this.added = added; - this.removed = removed; - } - - public BindingsResults getRemoved() { - return removed; - } - - public BindingsResults getAdded() { - return added; - } -} diff --git a/engine/src/test/java/engine/ConfigurationProvider.java b/engine/src/test/java/engine/ConfigurationProvider.java deleted file mode 100644 index a50a2815..00000000 --- a/engine/src/test/java/engine/ConfigurationProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -package engine; - -import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; - -import java.io.File; -import java.net.URL; - -public class ConfigurationProvider { - - public static SPARQL11SEProperties GetTestEnvConfiguration() throws SEPAPropertiesException { - SPARQL11SEProperties result; - final String configuaration = System.getProperty("testConfiguration"); - if( configuaration != null){ - final File confFile = new File(configuaration); - result = new SPARQL11SEProperties(confFile); - }else{ - URL config = Thread.currentThread().getContextClassLoader().getResource("dev.jsap"); - result = new SPARQL11SEProperties(new File(config.getPath())); - } - return result; - } -} diff --git a/engine/src/test/java/engine/ITUpdateProcessor.java b/engine/src/test/java/engine/ITUpdateProcessor.java deleted file mode 100644 index 5fde0610..00000000 --- a/engine/src/test/java/engine/ITUpdateProcessor.java +++ /dev/null @@ -1,64 +0,0 @@ -package engine; - -import it.unibo.arces.wot.sepa.api.SPARQL11SEProperties; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.sparql.Bindings; -import it.unibo.arces.wot.sepa.engine.processing.UpdateProcessor; -import it.unibo.arces.wot.sepa.engine.processing.UpdateResponseWithAR; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.*; - -public class ITUpdateProcessor { - - private UpdateProcessor updateProcessor; - - @Before - public void init() throws SEPAPropertiesException, SEPAProtocolException { - final SPARQL11SEProperties sparql11SEProperties = ConfigurationProvider.GetTestEnvConfiguration(); - updateProcessor = new UpdateProcessor(sparql11SEProperties, null); - } - @Test - public void testInsertAddRemoved(){ - UpdateRequest updateRequest = new UpdateRequest("INSERT{ }Where{}"); - Response process = updateProcessor.process(updateRequest, 5000); - - assertTrue("Not a UpdateResponse",process instanceof UpdateResponseWithAR); - UpdateResponseWithAR uAR = (UpdateResponseWithAR) process; - - assertTrue("Added is empty",!uAR.getAdded().isEmpty()); - assertTrue("Removed is not empty",uAR.getRemoved().isEmpty()); - - String sub = uAR.getAdded().getBindings().get(0).getBindingValue("subject"); - String pred = uAR.getAdded().getBindings().get(0).getBindingValue("predicate"); - String obj = uAR.getAdded().getBindings().get(0).getBindingValue("object"); - - assertEquals("test://it/update",sub); - assertEquals("test://it/update/pre",pred); - assertEquals("test://it/update/obj",obj); - } - - @Test - public void testDeleteAddRemoved(){ - UpdateRequest updateRequest = new UpdateRequest("DELETE{ }Where{}"); - Response process = updateProcessor.process(updateRequest, 5000); - - assertTrue("Not a UpdateResponse",process instanceof UpdateResponseWithAR); - UpdateResponseWithAR uAR = (UpdateResponseWithAR) process; - - assertTrue("Removed is empty",!uAR.getRemoved().isEmpty()); - assertTrue("Added is not empty",uAR.getAdded().isEmpty()); - - String sub = uAR.getRemoved().getBindings().get(0).getBindingValue("subject"); - String pred = uAR.getRemoved().getBindings().get(0).getBindingValue("predicate"); - String obj = uAR.getRemoved().getBindings().get(0).getBindingValue("object"); - - assertEquals("test://it/update",sub); - assertEquals("test://it/update/pre",pred); - assertEquals("test://it/update/obj",obj); - } -} diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java deleted file mode 100644 index dc057360..00000000 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import org.apache.jena.sparql.lang.arq.ParseException; -import org.apache.jena.update.Update; -import org.apache.jena.update.UpdateFactory; -import org.junit.Test; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import static org.junit.Assert.*; - -public class SPARQLAnalyzerTest { - private static final int DELETE_OUT = 1; - private static final int INSERT_OUT = 2; - private static final int INPUT = 0; - - private String [][] io = new String[][]{ - {"INSERT { }WHERE{}","","CONSTRUCT{.}WHERE{}"}, - {"DELETE WHERE{?a ?c}","CONSTRUCT{?a?c.}WHERE{?a?c}",""}, - {"CLEAR GRAPH ","CONSTRUCT{?s?p?o}WHERE{GRAPH{?s?p?o}.}",""}, - {"DELETE { }WHERE{?a ?b ?c}","CONSTRUCT{.}WHERE{?a?b?c}",""}, - {"DELETE { } INSERT { }WHERE{}","CONSTRUCT{.}WHERE{}","CONSTRUCT{.}WHERE{}"}, - {"DELETE { } INSERT { }WHERE{?a ?b ?c}","CONSTRUCT{.}WHERE{?a?b?c}","CONSTRUCT{.}WHERE{?a?b?c}"}, - {"INSERT DATA{ . }","","CONSTRUCT{..}WHERE{}"}, - {"DELETE DATA{ . }","CONSTRUCT{..}WHERE{}",""}, - }; - -//TODO Test move copy add operations - - @Test - public void setString() { - } - - @Test - public void getConstruct() { - for(String[] test : io){ - SPARQLAnalyzer sparqlAnalyzer = new SPARQLAnalyzer(test[INPUT]); - - UpdateConstruct construct = sparqlAnalyzer.getConstruct(); - assertEquals(test[DELETE_OUT], construct.getDeleteConstruct().replaceAll("\\s+","")); - - assertEquals(test[INSERT_OUT], construct.getInsertConstruct().replaceAll("\\s+","")); - } - } - - @Test - public void getLutt() { - } - - @Test - public void getConstructFromQuery() { - } -} \ No newline at end of file diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUMangerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java similarity index 89% rename from engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUMangerTest.java rename to engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java index 9f97aba1..c4c66495 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUMangerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/subscription/SPUMangerTest.java @@ -1,8 +1,10 @@ -package it.unibo.arces.wot.sepa.engine.processing.subscriptions; +package it.unibo.arces.wot.sepa.engine.processing.subscription; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.ISubscriptionProcUnit; +import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SpuManager; import org.junit.Assert; import org.junit.Before; import org.junit.Test; diff --git a/engine/src/test/resources/dev.jsap b/engine/src/test/resources/dev.jsap deleted file mode 100644 index 1266f08f..00000000 --- a/engine/src/test/resources/dev.jsap +++ /dev/null @@ -1,46 +0,0 @@ -{ - "host": "localhost", - "sparql11protocol": { - "protocol": "http", - "port": 8000, - "query": { - "path": "/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "ws", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/subscribe" - } - }, - "security": { - "register": "/oauth/register", - "tokenRequest": "/oauth/token", - "securePath": "/secure", - "client_id": "ryd7aRCosLlFYowQ5jid0lOlEdMhmbBFFCRZy6fA8KZtsQnuqhfg1zXbzCxpNPih", - "client_secret": "N5UgtT0EWMYV3aIsC+iyAB5kk2maR8MrEXV1+NaEocLFz4eYeVT1mKoRIDwgbpsJ", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oydQazps2cFzookIhydKJWfsRgSntEVGcQQR//AJ7psDRK5uCw8It/0FKvsuW0MAboo4X49sDS+AHTOnVUf67wnnPqJ2M1thThv3dIr/WNn+8xJovJWkwcpGP4T7nH7MOCfZzVnKTHr4hN3q14VUWHYmjDdzXXWr/jf1HAP4lTU6IeQqN57UDzW9Mr4Y/+5CnidJI5/KxklwtkuhlzeMQupyx34x2j1BAIJLuPIZHnU1oufaKo3I4b6us1FC0JxtftwpFmv5bnXCHKhsygqwQiqSoS6//kP9HugQjTW7b7kFcUsSriJhJNxztijHcYC1YSpLts3699qLcVmlh0+rF5lDncLv15C4CrA4Ro5BGp6AoAChOSB9WFxmivcFefl9V01XlsXMV64aVHzjFMWWXeLR0TXTDIih8LRJKFcAmM4M6ULqqyl9ELprG9axFSN2GINfkTopEKcGeCWEwJRbHxzor7qgxAzafboWDRb1J0DQxvTNuzK47b9i8QGRiWVkiS6rGnAArdIIiLLPzqOfR0/aGwuFrHfGoxxrJJvatKH9+IwZKW/lSg5XU23MSqKrLALOCInXDVMiK25w0nAff4P0hAnCEkUzkntRbGTrcPuUV8j298e7/NmelTlHqw8xZMf+0iZMBQrgzuXWni4Ok9+bMu65HZ4pBHEBJz5296s093a9/GHcRZaL6jUwd31/MD08PdBu6SYUVS7YKqND6U1jKt9w25i4xnOoJ9sBPcIMDCmgia/gqjR8kaTSgABEogibvwQvDnLpgcHKkh7U5lRegzxeDVO1V63Xj1tJgeGbB", - "expires": "QWAE7shP6t6lAuzmw5A8ZQ==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - } - }, - "graphs": { - "default-graph-uri": "http://default", - "named-graph-uri": "http://default", - "using-graph-uri": "http://default", - "using-named-graph-uri": "http://default" - } -} \ No newline at end of file From fdbaf3a6a93cad546a1f84cc7a8120dcf1cc290c Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 13 Sep 2018 16:22:15 +0200 Subject: [PATCH 51/76] Fix wrong wget url for virtuoso travis tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2da5d6aa..41aab8d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ jobs: before_script: - docker run --name my-virtuoso -p 8890:8890 -p 1111:1111 -e DBA_PASSWORD=myDbaPassword -e SPARQL_UPDATE=true -e DEFAULT_GRAPH=http://www.example.com/my-graph -d tenforce/virtuoso - sleep 30 - - wget http://localhost:9999/blazegraph/namespace/kb/sparql + - wget http://localhost:8890/sparql - mvn package - cd engine/target - mv endpoints/virtuoso.jpar endpoint.jpar From 36ed541a32027abf62f5b6e381622c218e8eda69 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 13 Sep 2018 16:37:58 +0200 Subject: [PATCH 52/76] Fix wrong virtuoso jpar path --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 41aab8d9..65c7d541 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ jobs: - wget http://localhost:8890/sparql - mvn package - cd engine/target - - mv endpoints/virtuoso.jpar endpoint.jpar + - mv endpoints/endpoint-virtuoso.jpar endpoint.jpar - java -jar engine-0-SNAPSHOT.jar > log.txt & - sleep 30 - cd ../.. From 6350ba1f5b616827e353040fc6a59a433be4326f Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 19 Sep 2018 16:09:30 +0200 Subject: [PATCH 53/76] Test both with Virtuoso and Blazegraph --- .../WebsocketSubscriptionProtocol.java | 14 +- .../commons/protocol/SPARQL11Protocol.java | 4 +- client-api/src/main/resources/log4j2.xml | 2 +- .../src/main/resources/sepatest-secure.jsap | 76 +++++ client-api/src/main/resources/sepatest.jsap | 76 +++++ .../arces/wot/sepa/ConfigurationProvider.java | 118 +++++++ .../wot/sepa/api/ConfigurationProvider.java | 29 -- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 201 ++++------- .../unibo/arces/wot/sepa/api/Publisher.java | 32 +- .../unibo/arces/wot/sepa/api/Subscriber.java | 68 ++-- .../websocket/ConfigurationProvider.java | 29 -- .../websocket/ITSEPAWebsocketClient.java | 4 +- .../protocol/websocket/ITWebSocketClient.java | 4 +- .../ITWebSocketSubscriptionProtocol.java | 84 ++--- .../security/ConfigurationProvider.java | 29 -- .../security/ITSEPASecurityManager.java | 6 +- .../src/test/resources/sepatest-secure.jsap | 18 +- client-api/src/test/resources/sepatest.jsap | 24 +- .../it/arces/wot/sepa/engine/gates/Gate.java | 176 ++++++++++ .../wot/sepa/engine/gates/SecureGate.java | 122 +++++++ .../wot/sepa/engine/gates/WebsocketGate.java | 54 +++ .../wot/sepa/engine/bean/EngineBeans.java | 2 +- ...ocessorBeans.java => SPUManagerBeans.java} | 2 +- .../arces/wot/sepa/engine/core/Engine.java | 323 ++++++++---------- .../sepa/engine/core/EngineProperties.java | 51 ++- .../sepa/engine/core/EngineShutdownHook.java | 11 +- .../wot/sepa/engine/core/ResponseHandler.java | 14 +- .../dependability/AuthorizationManager.java | 176 ++-------- .../engine/dependability/CORSManager.java | 4 +- .../engine/dependability/Dependability.java | 88 +++++ .../dependability/DependabilityManager.java | 70 ---- .../dependability/DependabilityMonitor.java | 88 +++++ ...an.java => DependabilityMonitorMBean.java} | 6 +- .../dependability/SEPASecurityContext.java | 14 + .../dependability/SubscriptionManager.java | 102 ++++++ .../wot/sepa/engine/processing/Processor.java | 40 ++- .../processing/QueryProcessingThread.java | 4 +- .../processing/SubscribeProcessingThread.java | 287 +--------------- .../processing/UpdateProcessingThread.java | 8 +- .../engine/processing/subscriptions/ISPU.java | 2 +- .../engine/processing/subscriptions/SPU.java | 28 +- .../processing/subscriptions/SPUManager.java | 322 ++++++++++++----- .../SPUManagerMBean.java} | 4 +- .../processing/subscriptions/SPUNaive.java | 12 +- .../processing/subscriptions/Subscriber.java | 45 +++ .../sepa/engine/protocol/http/HttpsGate.java | 17 +- .../http/handler/JWTRequestHandler.java | 10 +- .../http/handler/RegisterHandler.java | 8 +- .../http/handler/SPARQL11Handler.java | 6 +- .../http/handler/SPARQL11ResponseHandler.java | 2 +- .../http/handler/SecureQueryHandler.java | 10 +- .../http/handler/SecureUpdateHandler.java | 10 +- .../websocket/SecureWebsocketServer.java | 183 ++-------- .../websocket/SecureWebsocketServerMBean.java | 5 - .../websocket/WebsocketEventHandler.java | 48 --- .../protocol/websocket/WebsocketRequest.java | 21 -- .../protocol/websocket/WebsocketServer.java | 215 +++--------- .../scheduling/InternalQueryRequest.java | 6 - .../engine/scheduling/InternalRequest.java | 1 - .../scheduling/InternalSubscribeRequest.java | 18 +- .../engine/scheduling/InternalUQRequest.java | 7 + .../InternalUnsubscribeRequest.java | 20 +- .../scheduling/InternalUpdateRequest.java | 6 - .../wot/sepa/engine/scheduling/Scheduler.java | 134 ++++---- .../engine/scheduling/SchedulerQueue.java | 2 +- .../test/resources/endpoint-blazegraph.jpar | 33 ++ .../src/test/resources/endpoint-virtuoso.jpar | 33 ++ engine/src/test/resources/engine-secure.jpar | 3 +- engine/src/test/resources/engine.jpar | 3 +- 69 files changed, 1921 insertions(+), 1753 deletions(-) create mode 100644 client-api/src/main/resources/sepatest-secure.jsap create mode 100644 client-api/src/main/resources/sepatest.jsap create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java create mode 100644 engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java create mode 100644 engine/src/main/java/it/arces/wot/sepa/engine/gates/SecureGate.java create mode 100644 engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/{SubscribeProcessorBeans.java => SPUManagerBeans.java} (98%) create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Dependability.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitor.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/{AuthorizationManagerMBean.java => DependabilityMonitorMBean.java} (93%) create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SEPASecurityContext.java create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SubscriptionManager.java rename engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/{SubscribeProcessingThreadMBean.java => subscriptions/SPUManagerMBean.java} (85%) create mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServerMBean.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketRequest.java create mode 100644 engine/src/test/resources/endpoint-blazegraph.jpar create mode 100644 engine/src/test/resources/endpoint-virtuoso.jpar diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java index 185b9dbe..470a23ec 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java @@ -2,7 +2,6 @@ import java.net.URI; import java.net.URISyntaxException; -import java.nio.channels.NotYetConnectedException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -66,7 +65,7 @@ public WebsocketSubscriptionProtocol(String host, String path, ISubscriptionHand @Override public void subscribe(SubscribeRequest request) throws SEPAProtocolException { - logger.debug("@subscribe: "+request); + logger.trace("@subscribe: "+request); if (!client.isOpen()) { logger.debug("connectBlocking..."); try { @@ -77,10 +76,10 @@ public void subscribe(SubscribeRequest request) throws SEPAProtocolException { } try { - logger.debug("Send"); + logger.trace("Send"); client.send(request.toString()); - } catch (NotYetConnectedException e) { - logger.error(e.getMessage()); + } catch (Exception e) { + logger.error("Websocket send exception: "+e.getMessage()); throw new SEPAProtocolException(e); } } @@ -89,9 +88,10 @@ public void subscribe(SubscribeRequest request) throws SEPAProtocolException { public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { logger.debug("@unsubscribe: "+request); try { + logger.trace("Send"); client.send(request.toString()); - } catch (NotYetConnectedException e) { - logger.error(e.getMessage()); + } catch (Exception e) { + logger.error("Websocket send exception: "+e.getMessage()); throw new SEPAProtocolException(e); } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index 8eab6ec6..e8c469c8 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -160,7 +160,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { ret = new JsonParser().parse(responseBody).getAsJsonObject(); } catch(Exception e) { - return new ErrorResponse(HttpStatus.SC_UNPROCESSABLE_ENTITY,"JsonParseException","Not a valid JSON: "+responseBody); + return new ErrorResponse(responseCode,"JsonParsingException",e.getMessage()+" Response body: "+responseBody); } return new ErrorResponse(ret.get("status_code").getAsInt(), ret.get("error").getAsString(),ret.get("error_description").getAsString()); } @@ -173,7 +173,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { ret = new JsonParser().parse(responseBody).getAsJsonObject(); } catch(JsonParseException e) { - return new ErrorResponse(HttpStatus.SC_UNPROCESSABLE_ENTITY, "JsonParseException",e.getMessage()); + return new ErrorResponse(HttpStatus.SC_UNPROCESSABLE_ENTITY,"JsonParsingException", e.getMessage() +" Response body: "+responseBody); } return new QueryResponse(ret); } diff --git a/client-api/src/main/resources/log4j2.xml b/client-api/src/main/resources/log4j2.xml index 3be39197..c1b91c28 100644 --- a/client-api/src/main/resources/log4j2.xml +++ b/client-api/src/main/resources/log4j2.xml @@ -23,7 +23,7 @@ ALL Integer.MAX_VALUE - + diff --git a/client-api/src/main/resources/sepatest-secure.jsap b/client-api/src/main/resources/sepatest-secure.jsap new file mode 100644 index 00000000..780f8406 --- /dev/null +++ b/client-api/src/main/resources/sepatest-secure.jsap @@ -0,0 +1,76 @@ +{ + "host": "localhost", + "oauth": { + "enable": true, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "https", + "port": 8443, + "query": { + "path": "/secure/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/secure/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "wss", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://sepatest", + "named-graph-uri": "http://sepatest", + "using-graph-uri": "http://sepatest", + "using-named-graph-uri": "http://sepatest" + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "updates": { + "DELETE_ALL" : { + "sparql" : "delete where {?x ?y ?z}" + }, + "VAIMEE": { + "sparql": "delete {sepa:SV sepa:PV ?o} where {sepa:SV sepa:PV ?o} ; insert data {sepa:SV sepa:PV \"ვაიმეე\"}" + }, + "RANDOM": { + "sparql": "delete {sepa:S sepa:P ?o} where {sepa:S sepa:P ?o} ; insert {sepa:S sepa:P ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + }, + "RANDOM1": { + "sparql": "delete {sepa:S1 sepa:P1 ?o} where {sepa:S1 sepa:P1 ?o} ; insert {sepa:S1 sepa:P1 ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + } + }, + "queries": { + "VAIMEE": { + "sparql": "select * where {?x ?y \"ვაიმეე\"}" + }, + "ALL": { + "sparql": "select * where {?x ?y ?z}" + }, + "RANDOM": { + "sparql": "select * where {sepa:S sepa:P ?random}" + }, + "RANDOM1": { + "sparql": "select * where {sepa:S1 sepa:P1 ?random}" + }, + "COUNT": { + "sparql": "select (COUNT(?x) AS ?n) where {?x ?y ?z}" + } + } +} diff --git a/client-api/src/main/resources/sepatest.jsap b/client-api/src/main/resources/sepatest.jsap new file mode 100644 index 00000000..20a5cd76 --- /dev/null +++ b/client-api/src/main/resources/sepatest.jsap @@ -0,0 +1,76 @@ +{ + "host": "localhost", + "oauth": { + "enable" : false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8000, + "query": { + "path": "/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "ws", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "graphs": { + "default-graph-uri": "http://sepatest", + "named-graph-uri": "http://sepatest", + "using-graph-uri": "http://sepatest", + "using-named-graph-uri": "http://sepatest" + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "updates": { + "DELETE_ALL" : { + "sparql" : "delete where {?x ?y ?z}" + }, + "VAIMEE": { + "sparql": "delete {sepa:SV sepa:PV ?o} where {sepa:SV sepa:PV ?o} ; insert data {sepa:SV sepa:PV \"ვაიმეე\"}" + }, + "RANDOM": { + "sparql": "delete {sepa:S sepa:P ?o} where {sepa:S sepa:P ?o} ; insert {sepa:S sepa:P ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + }, + "RANDOM1": { + "sparql": "delete {sepa:S1 sepa:P1 ?o} where {sepa:S1 sepa:P1 ?o} ; insert {sepa:S1 sepa:P1 ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + } + }, + "queries": { + "VAIMEE": { + "sparql": "select * where {?x ?y \"ვაიმეე\"}" + }, + "ALL": { + "sparql": "select * where {?x ?y ?z}" + }, + "RANDOM": { + "sparql": "select * where {sepa:S sepa:P ?random}" + }, + "RANDOM1": { + "sparql": "select * where {sepa:S1 sepa:P1 ?random}" + }, + "COUNT": { + "sparql": "select (COUNT(?x) AS ?n) where {?x ?y ?z}" + } + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java new file mode 100644 index 00000000..22562324 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java @@ -0,0 +1,118 @@ +package it.unibo.arces.wot.sepa; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.request.QueryRequest; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import java.io.File; +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ConfigurationProvider { + protected final Logger logger = LogManager.getLogger(); + + private final JSAP appProfile; + private String prefixes = ""; + + public ConfigurationProvider() throws SEPAPropertiesException, SEPASecurityException { + String jsapFileName = "sepatest.jsap"; + + if (System.getProperty("testConfiguration") != null) { + jsapFileName = System.getProperty("testConfiguration"); + logger.info("JSAP from property testConfiguration: " + jsapFileName); + } else if (System.getProperty("secure") != null) { + jsapFileName = "sepatest-secure.jsap"; + logger.info("JSAP secure default: " + jsapFileName); + } + + String path = getClass().getClassLoader().getResource(jsapFileName).getPath(); + File f = new File(path); + if (!f.exists()) { + logger.error("File not found: " + path); + throw new SEPAPropertiesException("File not found: "+path); + } + + appProfile = new JSAP(path); + + Set appPrefixes = appProfile.getPrefixes(); + for (String prefix : appPrefixes) { + prefixes += "PREFIX " + prefix + ":<" + appProfile.getNamespaceURI(prefix) + "> "; + } + } + + private String getSPARQLUpdate(String id) { + return prefixes + " " +appProfile.getSPARQLUpdate(id); + } + + private String getSPARQLQuery(String id) { + return prefixes + " " +appProfile.getSPARQLQuery(id); + } + + public UpdateRequest buildUpdateRequest(String id, long timeout,SEPASecurityManager sm) { + String authorization = null; + + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + + return new UpdateRequest(appProfile.getUpdateMethod(id), appProfile.getUpdateProtocolScheme(id), + appProfile.getUpdateHost(id), appProfile.getUpdatePort(id), appProfile.getUpdatePath(id), + getSPARQLUpdate(id), appProfile.getUsingGraphURI(id), appProfile.getUsingNamedGraphURI(id), + authorization, timeout); + } + + public QueryRequest buildQueryRequest(String id, long timeout,SEPASecurityManager sm) { + String authorization = null; + + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + + return new QueryRequest(appProfile.getQueryMethod(id), appProfile.getQueryProtocolScheme(id), + appProfile.getQueryHost(id), appProfile.getQueryPort(id), appProfile.getQueryPath(id), + getSPARQLQuery(id), appProfile.getDefaultGraphURI(id), appProfile.getNamedGraphURI(id), + authorization, timeout); + } + + public SubscribeRequest buildSubscribeRequest(String id, long timeout,SEPASecurityManager sm) { + String authorization = null; + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + + return new SubscribeRequest(getSPARQLQuery(id), id, appProfile.getDefaultGraphURI(id), + appProfile.getNamedGraphURI(id), authorization, timeout); + } + + public UnsubscribeRequest buildUnsubscribeRequest(String spuid, long timeout,SEPASecurityManager sm) { + String authorization = null; + if (sm != null) + try { + authorization = sm.getAuthorizationHeader(); + } catch (SEPASecurityException | SEPAPropertiesException e) { + logger.error(e.getMessage()); + } + + return new UnsubscribeRequest(spuid, authorization, timeout); + } + + public JSAP getJsap() { + return appProfile; + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java deleted file mode 100644 index a53998a4..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ConfigurationProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import java.net.URL; - -public class ConfigurationProvider { - - public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - String jsapFileName = null; - - if( System.getProperty("testConfiguration") != null){ - jsapFileName = System.getProperty("testConfiguration"); - } - else if (System.getenv("testConfiguration") != null) { - jsapFileName = System.getenv("testConfiguration"); - }else if (System.getProperty("secure") != null){ - jsapFileName = System.getenv("sepatest-secure.jsap"); - }else{ - jsapFileName = System.getenv("sepatest.jsap"); - } - - URL config = Thread.currentThread().getContextClassLoader().getResource(jsapFileName); - - return new JSAP(config.getPath()); - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index f0fad979..746ee676 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -1,11 +1,11 @@ package it.unibo.arces.wot.sepa.api; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.request.QueryRequest; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; + import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; @@ -41,9 +41,12 @@ public class ITSPARQL11SEProtocol { private final ArrayList subscribers = new ArrayList(); private final ArrayList publishers = new ArrayList(); + private static ConfigurationProvider provider; + @BeforeClass public static void init() throws Exception { - properties = ConfigurationProvider.GetTestEnvConfiguration(); + provider = new ConfigurationProvider(); + properties = provider.getJsap(); if (properties.isSecure()) { sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", @@ -57,7 +60,7 @@ public static void init() throws Exception { @Before public void beginTest() throws IOException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, - URISyntaxException { + URISyntaxException, InterruptedException { sync.reset(); @@ -69,12 +72,12 @@ public void beginTest() throws IOException, SEPAProtocolException, SEPAPropertie subscribers.clear(); publishers.clear(); - Response ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + Response ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); } } @@ -88,6 +91,7 @@ public void endTest() throws IOException, InterruptedException { for (Subscriber sub : subscribers) sub.close(); + for (Publisher pub : publishers) { pub.finish(); pub.interrupt(); @@ -114,24 +118,24 @@ public void Register() throws SEPASecurityException, SEPAPropertiesException { @Test(timeout = 5000) public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { // Delete all triples - Response ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + Response ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - ret = client.update(buildUpdateRequest("DELETE_ALL", 5000)); + ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); } } assertFalse(String.valueOf(ret), ret.isError()); // Evaluate if the store is empty - ret = client.query(buildQueryRequest("COUNT", 5000)); + ret = client.query(provider.buildQueryRequest("COUNT", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - ret = client.query(buildQueryRequest("COUNT", 5000)); + ret = client.query(provider.buildQueryRequest("COUNT", 5000,sm)); } } @@ -149,21 +153,21 @@ public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, I @Test(timeout = 25000) public void RequestToken() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { if (sm != null) { - for (int i = 0; i < 20; i++) { + for (int i = 0; i < 2000; i++) { String authorization = sm.getAuthorizationHeader(); assertFalse("Failed to get authorization header", authorization == null); - Thread.sleep(1000); + Thread.sleep(10); } } } @Test(timeout = 5000) public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { - Response ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + Response ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); } } assertFalse(String.valueOf(ret), ret.isError()); @@ -171,11 +175,11 @@ public void Update() throws IOException, SEPAPropertiesException, SEPASecurityEx @Test(timeout = 5000) public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { - Response ret = client.query(buildQueryRequest("ALL", 5000)); + Response ret = client.query(provider.buildQueryRequest("ALL", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - client.query(buildQueryRequest("ALL", 5000)); + client.query(provider.buildQueryRequest("ALL", 5000,sm)); } } assertFalse(String.valueOf(ret), ret.isError()); @@ -184,21 +188,21 @@ public void Query() throws IOException, SEPAPropertiesException, SEPASecurityExc @Test(timeout = 5000) public void UpdateAndQuery() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { - Response ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + Response ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - ret = client.update(buildUpdateRequest("VAIMEE", 5000)); + ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); } } assertFalse(String.valueOf(ret), ret.isError()); - ret = client.query(buildQueryRequest("VAIMEE", 5000)); + ret = client.query(provider.buildQueryRequest("VAIMEE", 5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; if (error.isTokenExpiredError()) { - client.query(buildQueryRequest("VAIMEE", 5000)); + client.query(provider.buildQueryRequest("VAIMEE", 5000,sm)); } } @@ -209,10 +213,10 @@ public void UpdateAndQuery() @Test(timeout = 5000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - subscribers.add(new Subscriber("ALL", properties, sm, sync)); + subscribers.add(new Subscriber("ALL", sm, sync)); for (Subscriber sub : subscribers) - sub.subscribe(); + sub.start(); sync.waitSubscribes(subscribers.size()); sync.waitEvents(subscribers.size()); @@ -229,15 +233,12 @@ public void Subscribe3xN() int n = 30; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("ALL", properties, sm, sync)); - subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); - subscribers.add(new Subscriber("RANDOM1", properties, sm, sync)); + subscribers.add(new Subscriber("ALL", sm, sync)); + subscribers.add(new Subscriber("RANDOM", sm, sync)); + subscribers.add(new Subscriber("RANDOM1", sm, sync)); } - for (Subscriber sub : subscribers) { - sub.subscribe(); - //Thread.sleep(500); - } + for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); sync.waitEvents(subscribers.size()); @@ -251,14 +252,13 @@ public void Subscribe3xN() @Test(timeout = 5000) public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - subscribers.add(new Subscriber("ALL", properties, sm, sync)); + subscribers.add(new Subscriber("ALL", sm, sync)); for (Subscriber sub : subscribers) - sub.subscribe(); + sub.start(); sync.waitSubscribes(subscribers.size()); - for (Subscriber sub : subscribers) - sub.unsubscribe(sync.getSpuid()); + for (Subscriber sub : subscribers) sub.unsubscribe(sync.getSpuid()); sync.waitEvents(subscribers.size()); sync.waitUnsubscribes(subscribers.size()); @@ -275,41 +275,37 @@ public void Unsubscribe() public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException { - subscribers.add(new Subscriber("VAIMEE", properties, sm, sync)); - for (Subscriber sub : subscribers) - sub.subscribe(); + subscribers.add(new Subscriber("VAIMEE", sm, sync)); + for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); - publishers.add(new Publisher("VAIMEE", properties, sm, 1)); - for (Publisher pub : publishers) - pub.start(); + publishers.add(new Publisher("VAIMEE", sm, 1)); + for (Publisher pub : publishers) pub.start(); - sync.waitEvents(2); + sync.waitEvents(1); assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", sync.getSubscribes() != subscribers.size()); - assertFalse("Events:" + sync.getEvents() + "(2)", sync.getEvents() != 2); + assertFalse("Events:" + sync.getEvents() + "(1)", sync.getEvents() != 1); } @Test(timeout = 60000) public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 10; + int n = 5; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); - publishers.add(new Publisher("RANDOM", properties, sm, n)); + subscribers.add(new Subscriber("RANDOM", sm, sync)); + publishers.add(new Publisher("RANDOM", sm, n)); } - for (Subscriber sub : subscribers) - sub.subscribe(); + for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); - for (Publisher pub : publishers) - pub.start(); + for (Publisher pub : publishers) pub.start(); sync.waitEvents(subscribers.size() + subscribers.size() * publishers.size() * publishers.size()); @@ -322,46 +318,33 @@ public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtoc } @Test(timeout = 60000) - public void UpdateHeavyLoad() throws InterruptedException { + public void UpdateHeavyLoad() throws InterruptedException, SEPAPropertiesException, SEPASecurityException { int n = 10; for (int i = 0; i < n; i++) { - publishers.add(new Publisher("RANDOM", properties, sm, n)); - publishers.add(new Publisher("RANDOM1", properties, sm, n)); - publishers.add(new Publisher("VAIMEE", properties, sm, n)); + publishers.add(new Publisher("RANDOM", sm, n)); + publishers.add(new Publisher("RANDOM1", sm, n)); + publishers.add(new Publisher("VAIMEE", sm, n)); } - for (Publisher pub : publishers) - pub.start(); - - for (Publisher pub : publishers) - pub.join(); - publishers.clear(); + for (Publisher pub : publishers) pub.start(); + for (Publisher pub : publishers) pub.join(); } - /* To be used for long lasting test*/ - //@Test + /* To be used for long lasting test (30 min)*/ + //@Test(timeout = 1800000) public void StressTest() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 10; + int n = 50; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("ALL", properties, sm, sync)); - subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); - subscribers.add(new Subscriber("RANDOM1", properties, sm, sync)); + subscribers.add(new Subscriber("ALL", sm, sync)); + subscribers.add(new Subscriber("RANDOM", sm, sync)); + subscribers.add(new Subscriber("RANDOM1", sm, sync)); } - new Thread() { - public void run() { - for (Subscriber sub : subscribers) - try { - sub.subscribe(); - } catch (SEPAProtocolException | InterruptedException e) { - logger.error(e.getMessage()); - } - } - }.start(); + for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); sync.waitEvents(subscribers.size()); @@ -375,15 +358,12 @@ public void run() { sync.reset(); for (int i = 0; i < n; i++) { - publishers.add(new Publisher("RANDOM", properties, sm, n)); - publishers.add(new Publisher("RANDOM1", properties, sm, n)); + publishers.add(new Publisher("RANDOM", sm, n)); + publishers.add(new Publisher("RANDOM1", sm, n)); } - for (Publisher pub : publishers) - pub.start(); - - for (Publisher pub : publishers) - pub.join(); + for (Publisher pub : publishers) pub.start(); + for (Publisher pub : publishers) pub.join(); sync.waitEvents(events); @@ -394,35 +374,24 @@ public void run() { @Test(timeout = 60000) public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 10; + int n = 15; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("ALL", properties, sm, sync)); - subscribers.add(new Subscriber("RANDOM", properties, sm, sync)); - subscribers.add(new Subscriber("RANDOM1", properties, sm, sync)); + subscribers.add(new Subscriber("ALL", sm, sync)); + subscribers.add(new Subscriber("RANDOM", sm, sync)); + subscribers.add(new Subscriber("RANDOM1", sm, sync)); - publishers.add(new Publisher("RANDOM", properties, sm, n)); - publishers.add(new Publisher("RANDOM1", properties, sm, n)); + publishers.add(new Publisher("RANDOM", sm, n)); + publishers.add(new Publisher("RANDOM1", sm, n)); } int events = 4 * n * n * n + subscribers.size(); - new Thread() { - public void run() { - for (Subscriber sub : subscribers) - try { - sub.subscribe(); - } catch (SEPAProtocolException | InterruptedException e) { - logger.error(e.getMessage()); - } - } - - }.start(); + for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); - for (Publisher pub : publishers) - pub.start(); + for (Publisher pub : publishers) pub.start(); sync.waitEvents(events); @@ -430,36 +399,4 @@ public void run() { sync.getSubscribes() != subscribers.size()); assertFalse("Events:" + sync.getEvents() + "(" + events + ")", sync.getEvents() != events); } - - private static UpdateRequest buildUpdateRequest(String id, long timeout) { - String authorization = null; - - if (sm != null) - try { - authorization = sm.getAuthorizationHeader(); - } catch (SEPASecurityException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - } - - return new UpdateRequest(properties.getUpdateMethod(id), properties.getUpdateProtocolScheme(id), - properties.getUpdateHost(id), properties.getUpdatePort(id), properties.getUpdatePath(id), - properties.getSPARQLUpdate(id), properties.getUsingGraphURI(id), properties.getUsingNamedGraphURI(id), - authorization, timeout); - } - - private static QueryRequest buildQueryRequest(String id, long timeout) { - String authorization = null; - - if (sm != null) - try { - authorization = sm.getAuthorizationHeader(); - } catch (SEPASecurityException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - } - - return new QueryRequest(properties.getQueryMethod(id), properties.getQueryProtocolScheme(id), - properties.getQueryHost(id), properties.getQueryPort(id), properties.getQueryPath(id), - properties.getSPARQLQuery(id), properties.getDefaultGraphURI(id), properties.getNamedGraphURI(id), - authorization, timeout); - } } \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java index f2cd418b..e078b4a7 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -5,27 +5,28 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import it.unibo.arces.wot.sepa.pattern.JSAP; + +import static org.junit.Assert.*; public class Publisher extends Thread { protected final Logger logger = LogManager.getLogger(); - private final JSAP properties; private final SEPASecurityManager sm; private final SPARQL11Protocol client; private final String id; private AtomicLong running; - public Publisher(String id,JSAP properties, SEPASecurityManager sm,long n) { - this.properties = properties; + private static ConfigurationProvider provider; + + public Publisher(String id,SEPASecurityManager sm,long n) throws SEPAPropertiesException, SEPASecurityException { this.sm = sm; this.id = id; @@ -35,17 +36,21 @@ public Publisher(String id,JSAP properties, SEPASecurityManager sm,long n) { client = new SPARQL11Protocol(); running = new AtomicLong(n); + + provider = new ConfigurationProvider(); } public void run() { while(running.get() > 0) { - Response ret = client.update(buildUpdateRequest(id,5000)); + Response ret = client.update(provider.buildUpdateRequest(id,5000,sm)); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; logger.error(error); if (error.isTokenExpiredError()) { - client.update(buildUpdateRequest(id,5000)); + client.update(provider.buildUpdateRequest(id,5000,sm)); } + else + assertFalse(error.toString(),true); } running.set(running.get()-1); } @@ -54,17 +59,4 @@ public void run() { public void finish() { running.set(0); } - - protected UpdateRequest buildUpdateRequest(String id, int timeout) { - String authorization = null; - if (sm != null) - try { - authorization = sm.getAuthorizationHeader(); - } catch (SEPASecurityException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - } - - return new UpdateRequest(properties.getUpdateMethod(id), properties.getUpdateProtocolScheme(id), properties.getUpdateHost(id), properties.getUpdatePort(id), properties.getUpdatePath(id), properties.getSPARQLUpdate(id), properties.getUsingGraphURI(id), properties.getUsingNamedGraphURI(id), - authorization,timeout); - } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java index b5e5107b..05de21e4 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -5,6 +5,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; @@ -12,17 +13,13 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import it.unibo.arces.wot.sepa.pattern.JSAP; -public class Subscriber implements ISubscriptionHandler { +public class Subscriber extends Thread implements ISubscriptionHandler { protected final Logger logger = LogManager.getLogger(); - private final JSAP properties; private final SEPASecurityManager sm; private final SPARQL11SEProtocol client; private final String id; @@ -32,37 +29,51 @@ public class Subscriber implements ISubscriptionHandler { private AtomicBoolean subscribing = new AtomicBoolean(false); private AtomicBoolean unsubscribing = new AtomicBoolean(false); + private static ConfigurationProvider provider; - public Subscriber(String id, JSAP properties, SEPASecurityManager sm, Sync sync) throws SEPAProtocolException, SEPASecurityException { - this.properties = properties; + public Subscriber(String id,SEPASecurityManager sm, Sync sync) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + provider = new ConfigurationProvider(); + this.sm = sm; this.id = id; this.sync = sync; SubscriptionProtocol protocol; if (sm != null) { - protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), - properties.getSubscribePath(), sm, this); + protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath(), sm, this); } else { - protocol = new WebsocketSubscriptionProtocol(properties.getDefaultHost(), properties.getSubscribePort(), - properties.getSubscribePath(), this); + protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath(), this); } client = new SPARQL11SEProtocol(protocol); } + + public void run() { + try { + subscribe(); + synchronized(this) { + wait(); + } + } catch (SEPAProtocolException | InterruptedException e) { + + } + } public void close() { client.close(); + interrupt(); } - public void subscribe() throws SEPAProtocolException, InterruptedException { + private void subscribe() throws SEPAProtocolException, InterruptedException { subscribing.set(true); - client.subscribe(buildSubscribeRequest(id, 5000)); + client.subscribe(provider.buildSubscribeRequest(id, 5000,sm)); } public void unsubscribe(String spuid) throws SEPAProtocolException, InterruptedException { unsubscribing.set(true); - client.unsubscribe(buildUnsubscribeRequest(spuid, 5000)); + client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000,sm)); } @Override @@ -83,13 +94,13 @@ public void onError(ErrorResponse errorResponse) { if (errorResponse.isTokenExpiredError()) { if (subscribing.get()) try { - client.subscribe(buildSubscribeRequest(id, 5000)); + client.subscribe(provider.buildSubscribeRequest(id, 5000,sm)); } catch (SEPAProtocolException e) { logger.error(e.getMessage()); } else if (unsubscribing.get()) try { - client.unsubscribe(buildUnsubscribeRequest(spuid, 5000)); + client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000,sm)); } catch (SEPAProtocolException e) { logger.error(e.getMessage()); } @@ -111,29 +122,4 @@ public void onUnsubscribe(String spuid) { unsubscribing.set(false); sync.unsubscribe(); } - - private SubscribeRequest buildSubscribeRequest(String id, long timeout) { - String authorization = null; - if (sm != null) - try { - authorization = sm.getAuthorizationHeader(); - } catch (SEPASecurityException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - } - - return new SubscribeRequest(properties.getSPARQLQuery(id), id, properties.getDefaultGraphURI(id), - properties.getNamedGraphURI(id), authorization, timeout); - } - - private UnsubscribeRequest buildUnsubscribeRequest(String spuid, long timeout) { - String authorization = null; - if (sm != null) - try { - authorization = sm.getAuthorizationHeader(); - } catch (SEPASecurityException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - } - - return new UnsubscribeRequest(spuid, authorization, timeout); - } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java deleted file mode 100644 index 5d77b1d0..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ConfigurationProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import java.net.URL; - -public class ConfigurationProvider { - - public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - String jsapFileName = null; - - if( System.getProperty("testConfiguration") != null){ - jsapFileName = System.getProperty("testConfiguration"); - } - else if (System.getenv("testConfiguration") != null) { - jsapFileName = System.getenv("testConfiguration"); - }else if (System.getProperty("secure") != null){ - jsapFileName = System.getenv("sepatest-secure.jsap"); - }else{ - jsapFileName = System.getenv("sepatest.jsap"); - } - - URL config = Thread.currentThread().getContextClassLoader().getResource(jsapFileName); - - return new JSAP(config.getPath()); - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java index cdd71344..3b495764 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java @@ -11,7 +11,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import it.unibo.arces.wot.sepa.api.ConfigurationProvider; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.protocols.websocket.SEPAWebsocketClient; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -29,7 +29,7 @@ public class ITSEPAWebsocketClient implements ISubscriptionHandler { @BeforeClass public static void init() throws Exception { - properties = ConfigurationProvider.GetTestEnvConfiguration(); + properties = new ConfigurationProvider().getJsap(); if(properties.isSecure()) { int port = properties.getSubscribePort(); if (port == -1) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java index 366b2bbc..a8295664 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java @@ -11,7 +11,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import it.unibo.arces.wot.sepa.api.ConfigurationProvider; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.pattern.JSAP; @@ -53,7 +53,7 @@ public void onError(Exception ex) { @BeforeClass public static void init() throws Exception { - properties = ConfigurationProvider.GetTestEnvConfiguration(); + properties = new ConfigurationProvider().getJsap(); if(properties.isSecure()) { int port = properties.getSubscribePort(); if (port == -1) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 563dd46d..46602ff1 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -11,7 +11,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import it.unibo.arces.wot.sepa.api.ConfigurationProvider; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; @@ -22,25 +22,25 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import it.unibo.arces.wot.sepa.pattern.JSAP; import static org.junit.Assert.*; public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { protected final Logger logger = LogManager.getLogger(); - private static JSAP app = null; private WebsocketSubscriptionProtocol client = null; private static SEPASecurityManager sm = null; private static AtomicLong subscribes = new AtomicLong(0); private String spuid = null; + private static ConfigurationProvider provider; + @BeforeClass - public static void init() throws SEPAPropertiesException, SEPASecurityException { - app = ConfigurationProvider.GetTestEnvConfiguration(); + public static void init() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { + provider = new ConfigurationProvider(); - if (app.isSecure()) { - sm = new SEPASecurityManager(app.getAuthenticationProperties()); + if (provider.getJsap().isSecure()) { + sm = new SEPASecurityManager(provider.getJsap().getAuthenticationProperties()); sm.register("SEPATest"); } } @@ -48,12 +48,12 @@ public static void init() throws SEPAPropertiesException, SEPASecurityException @Before public void before() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, URISyntaxException { - if (app.isSecure()) { - client = new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), - app.getSubscribePath(), sm, this); + if (provider.getJsap().isSecure()) { + client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath(), sm, this); } else - client = new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), - app.getSubscribePath(), this); + client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath(), this); subscribes.set(0); } @@ -65,13 +65,7 @@ public void after() { @Test(timeout = 5000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - SubscribeRequest request; - if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); - } else - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), null, 5000); + SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 5000, sm); logger.debug(request); @@ -94,15 +88,9 @@ public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, S @Test(timeout = 5000) public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - int n = 500; + int n = 50; - SubscribeRequest request; - if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 500); - } else - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), null, 500); + SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 5000, sm); for (int i = 0; i < n; i++) { logger.debug(request); @@ -126,18 +114,18 @@ public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, @Test(timeout = 10000) public void SubscribeMxN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - int n = 50; - int m = 20; + int n = 10; + int m = 5; ArrayList clients = new ArrayList(); for (int i = 0; i < m; i++) { - if (app.isSecure()) { - clients.add(new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), - app.getSubscribePath(), sm, this)); + if (provider.getJsap().isSecure()) { + clients.add(new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath(), sm, this)); } else - clients.add(new WebsocketSubscriptionProtocol(app.getDefaultHost(), app.getSubscribePort(), - app.getSubscribePath(), this)); + clients.add(new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath(), this)); } for (int i = 0; i < m; i++) { @@ -170,17 +158,8 @@ public Subscriber(WebsocketSubscriptionProtocol client, int n) { public void run() { for (int j = 0; j < n; j++) { - SubscribeRequest request = null; - if (app.isSecure()) { - try { - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 500); - } catch (SEPASecurityException | SEPAPropertiesException e) { - logger.error(e.getMessage()); - } - } else - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), null, 500); + SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 500, sm); + logger.debug(request); try { client.subscribe(request); @@ -193,13 +172,7 @@ public void run() { @Test(timeout = 5000) public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - SubscribeRequest request; - if (app.isSecure()) { - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), sm.getAuthorizationHeader(), 5000); - } else - request = new SubscribeRequest(app.getSPARQLQuery("RANDOM"), "RANDOM", app.getDefaultGraphURI("RANDOM"), - app.getNamedGraphURI("RANDOM"), null, 5000); + SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 500, sm); spuid = null; client.subscribe(request); @@ -216,12 +189,7 @@ public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecuri assertFalse("Failed to subscribe", subscribes.get() != 1); - UnsubscribeRequest unsub; - if (app.isSecure()) { - unsub = new UnsubscribeRequest(spuid, sm.getAuthorizationHeader(), 5000); - } else { - unsub = new UnsubscribeRequest(spuid, null, 5000); - } + UnsubscribeRequest unsub = provider.buildUnsubscribeRequest(spuid, 500, sm); client.unsubscribe(unsub); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java deleted file mode 100644 index baf0d712..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ConfigurationProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package it.unibo.arces.wot.sepa.commons.security; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import java.net.URL; - -public class ConfigurationProvider { - - public static JSAP GetTestEnvConfiguration() throws SEPAPropertiesException, SEPASecurityException { - String jsapFileName = null; - - if( System.getProperty("testConfiguration") != null){ - jsapFileName = System.getProperty("testConfiguration"); - } - else if (System.getenv("testConfiguration") != null) { - jsapFileName = System.getenv("testConfiguration"); - }else if (System.getProperty("secure") != null){ - jsapFileName = System.getenv("sepatest-secure.jsap"); - }else{ - jsapFileName = System.getenv("sepatest.jsap"); - } - - URL config = Thread.currentThread().getContextClassLoader().getResource(jsapFileName); - - return new JSAP(config.getPath()); - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java index 80944ae7..9929f42e 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -3,7 +3,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import it.unibo.arces.wot.sepa.api.ConfigurationProvider; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -21,8 +21,8 @@ public class ITSEPASecurityManager { private String notAllowedId = "IamNotAllowedToRegister"; @BeforeClass - public static void init() throws SEPAPropertiesException, SEPASecurityException { - app = ConfigurationProvider.GetTestEnvConfiguration(); + public static void init() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { + app = new ConfigurationProvider().getJsap(); if (app.isSecure()) sm = new SEPASecurityManager(app.getAuthenticationProperties()); } diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index 780f8406..bb0793b1 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -44,33 +44,33 @@ }, "updates": { "DELETE_ALL" : { - "sparql" : "delete where {?x ?y ?z}" + "sparql" : "DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }" }, "VAIMEE": { - "sparql": "delete {sepa:SV sepa:PV ?o} where {sepa:SV sepa:PV ?o} ; insert data {sepa:SV sepa:PV \"ვაიმეე\"}" + "sparql": "DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}" }, "RANDOM": { - "sparql": "delete {sepa:S sepa:P ?o} where {sepa:S sepa:P ?o} ; insert {sepa:S sepa:P ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" }, "RANDOM1": { - "sparql": "delete {sepa:S1 sepa:P1 ?o} where {sepa:S1 sepa:P1 ?o} ; insert {sepa:S1 sepa:P1 ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" } }, "queries": { "VAIMEE": { - "sparql": "select * where {?x ?y \"ვაიმეე\"}" + "sparql": "SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}" }, "ALL": { - "sparql": "select * where {?x ?y ?z}" + "sparql": "SELECT * WHERE {GRAPH {?x ?y ?z}}" }, "RANDOM": { - "sparql": "select * where {sepa:S sepa:P ?random}" + "sparql": "SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}" }, "RANDOM1": { - "sparql": "select * where {sepa:S1 sepa:P1 ?random}" + "sparql": "SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}" }, "COUNT": { - "sparql": "select (COUNT(?x) AS ?n) where {?x ?y ?z}" + "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" } } } diff --git a/client-api/src/test/resources/sepatest.jsap b/client-api/src/test/resources/sepatest.jsap index 20a5cd76..3ac7e305 100644 --- a/client-api/src/test/resources/sepatest.jsap +++ b/client-api/src/test/resources/sepatest.jsap @@ -32,45 +32,39 @@ } } }, - "graphs": { - "default-graph-uri": "http://sepatest", - "named-graph-uri": "http://sepatest", - "using-graph-uri": "http://sepatest", - "using-named-graph-uri": "http://sepatest" - }, "namespaces": { "sepa": "http://wot.arces.unibo.it/sepa#", "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }, "updates": { "DELETE_ALL" : { - "sparql" : "delete where {?x ?y ?z}" + "sparql" : "DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }" }, "VAIMEE": { - "sparql": "delete {sepa:SV sepa:PV ?o} where {sepa:SV sepa:PV ?o} ; insert data {sepa:SV sepa:PV \"ვაიმეე\"}" + "sparql": "DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}" }, "RANDOM": { - "sparql": "delete {sepa:S sepa:P ?o} where {sepa:S sepa:P ?o} ; insert {sepa:S sepa:P ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" }, "RANDOM1": { - "sparql": "delete {sepa:S1 sepa:P1 ?o} where {sepa:S1 sepa:P1 ?o} ; insert {sepa:S1 sepa:P1 ?random} where {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + "sparql": "DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" } }, "queries": { "VAIMEE": { - "sparql": "select * where {?x ?y \"ვაიმეე\"}" + "sparql": "SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}" }, "ALL": { - "sparql": "select * where {?x ?y ?z}" + "sparql": "SELECT * WHERE {GRAPH {?x ?y ?z}}" }, "RANDOM": { - "sparql": "select * where {sepa:S sepa:P ?random}" + "sparql": "SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}" }, "RANDOM1": { - "sparql": "select * where {sepa:S1 sepa:P1 ?random}" + "sparql": "SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}" }, "COUNT": { - "sparql": "select (COUNT(?x) AS ?n) where {?x ?y ?z}" + "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" } } } diff --git a/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java b/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java new file mode 100644 index 00000000..8c81e311 --- /dev/null +++ b/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java @@ -0,0 +1,176 @@ +package it.arces.wot.sepa.engine.gates; + +import java.util.UUID; + +import org.apache.http.HttpStatus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.core.EventHandler; +import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; + +public abstract class Gate implements ResponseHandler, EventHandler { + private static final Logger logger = LogManager.getLogger(); + + protected final String gid; + protected final Scheduler scheduler; + + public abstract void send(String response) throws SEPAProtocolException; + + public Gate(Scheduler scheduler) { + this.scheduler = scheduler; + + gid = "sepa://gate/" + UUID.randomUUID(); + } + + public final String getGID() { + return gid; + } + + public final Scheduler getScheduler() { + return scheduler; + } + + public final void close() { + Dependability.onCloseGate(gid); + } + + public final void onError(Exception e) { + Dependability.onGateError(gid,e); + } + + @Override + public void notifyEvent(Notification notify) throws SEPAProtocolException { + logger.trace("@notifyEvent: " + notify); + send(notify.toString()); + } + + @Override + public void sendResponse(Response response) throws SEPAProtocolException { + logger.debug("@sendResponse: " + response); + send(response.toString()); + } + + public final void onMessage(String message) throws SEPAProtocolException { + // Parse the request + InternalRequest req = parseRequest(message); + if (req == null) { + logger.error("@onMessage " + getGID() + " failed to parse message: " + req); + ErrorResponse response = new ErrorResponse(400, "parsing_error", "Malformed request: " + req); + sendResponse(response); + return; + } + + // Schedule the request + logger.debug("@onMessage: " + getGID() + " schedule request: " + req); + ScheduledRequest request = scheduler.schedule(req, this); + + // Request not scheduled + if (request == null) { + logger.error("@onMessage: " + getGID() + " out of tokens"); + ErrorResponse response = new ErrorResponse(500, "too_many_requests", "Too many pending requests"); + sendResponse(response); + } + } + + /** + * SPARQL 1.1 Subscribe language + * + *
    +	{"subscribe":{
    +		"sparql":"SPARQL Query 1.1", 
    +		"authorization": "Bearer JWT", (optional)
    +		"alias":"an alias for the subscription", (optional)
    +		"default-graph-uri": "graphURI", (optional)
    +		"named-graph-uri": "graphURI" (optional)
    +	}}
    +	
    +	{"unsubscribe":{
    +		"spuid":"SPUID", 
    +		"authorization": "Bearer JWT" (optional)
    +	}}
    +	 * 
    + * + * @throws SEPAProtocolException + */ + protected InternalRequest parseRequest(String request) throws JsonParseException, JsonSyntaxException, + IllegalStateException, ClassCastException, SEPAProtocolException { + JsonObject req; + ErrorResponse error; + + try { + req = new JsonParser().parse(request).getAsJsonObject(); + } catch (JsonParseException e) { + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException", + "JsonParseException: " + request); + sendResponse(error); + logger.error(error); + return null; + } + + if (req.has("subscribe")) { + String sparql = null; + String alias = null; + String defaultGraphUri = null; + String namedGraphUri = null; + + try { + sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); + } catch (Exception e) { + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception", + "sparql member not found: " + request); + sendResponse(error); + logger.error(error); + return null; + } + + try { + alias = req.get("subscribe").getAsJsonObject().get("alias").getAsString(); + } catch (Exception e) { + } + + try { + defaultGraphUri = req.get("subscribe").getAsJsonObject().get("default-graph-uri").getAsString(); + } catch (Exception e) { + } + + try { + namedGraphUri = req.get("subscribe").getAsJsonObject().get("named-graph-uri").getAsString(); + } catch (Exception e) { + } + + return new InternalSubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri, this); + } else if (req.has("unsubscribe")) { + String spuid; + try { + spuid = req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString(); + } catch (Exception e) { + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception", "spuid member not found: " + request); + sendResponse(error); + return null; + } + + return new InternalUnsubscribeRequest(gid,spuid); + } + + error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unsupported", "Bad request: " + request); + sendResponse(error); + return null; + } + +} diff --git a/engine/src/main/java/it/arces/wot/sepa/engine/gates/SecureGate.java b/engine/src/main/java/it/arces/wot/sepa/engine/gates/SecureGate.java new file mode 100644 index 00000000..fc889fc1 --- /dev/null +++ b/engine/src/main/java/it/arces/wot/sepa/engine/gates/SecureGate.java @@ -0,0 +1,122 @@ +package it.arces.wot.sepa.engine.gates; + +import org.apache.http.HttpStatus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; + +public abstract class SecureGate extends Gate{ + private static final Logger logger = LogManager.getLogger(); + + public SecureGate(Scheduler scheduler) { + super(scheduler); + } + + @Override + protected InternalRequest parseRequest(String request) + throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException, SEPAProtocolException { + JsonObject req; + + try{ + req = new JsonParser().parse(request).getAsJsonObject(); + } + catch(Exception e) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","Exception: " + request); + send(error.toString()); + logger.error(error); + return null; + } + + // CHECK AUTHORIZATION + Response ret = validateRequest(req); + + if (ret.isError()) { + // Not authorized + logger.warn("NOT AUTHORIZED"); + send(ret.toString()); + return null; + } + + return super.parseRequest(request); + } +/** +
    +	Specific to SPARQL 1.1 SE Subscribe request:
    +	1. Check if the request contains an "authorization" member. 
    +	2. Check if the request contains an "authorization" member that start with "Bearer" 
    +	3. Check if the value of the "authorization" member is a JWT object ==> VALIDATE TOKEN
    +
    +	Token validation:
    +	4. Check if the JWT object is signed 
    +	5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    +	6. Check the contents of the JWT object 
    +	7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    +	8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    +	9. Accept the request as well as "sub" as the originator of the request and process it as usual
    +	 
    +	Respond with 401 if not
    +	 
    +	
    + */ + private Response validateRequest(JsonObject request) { + String bearer = null; + JsonObject subUnsub = null; + + if (request.has("subscribe")) subUnsub = request.get("subscribe").getAsJsonObject(); + else if (request.has("unsubscribe")) subUnsub = request.get("unsubscribe").getAsJsonObject(); + + if (subUnsub == null) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request","Neither subscribe or unsuscribe found"); + logger.error(error); + return error; + } + + if (!subUnsub.has("authorization")) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization member is missing"); + logger.error(error); + return error; + } + + try{ + bearer = subUnsub.get("authorization").getAsString(); + } + catch(Exception e) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization member is not a string"); + logger.error(error); + return error; + } + + if (!bearer.startsWith("Bearer ")) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization value MUST be of type Bearer"); + logger.error(error); + return error; + } + + String jwt = bearer.substring(7); + + if (jwt == null) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Token is null"); + logger.error(error); + return error; + } + if (jwt.equals("")) { + ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Token is empty"); + logger.error(error); + return error; + } + + // Token validation + return Dependability.validateToken(jwt); + } +} diff --git a/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java b/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java new file mode 100644 index 00000000..07c2ec41 --- /dev/null +++ b/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java @@ -0,0 +1,54 @@ +package it.arces.wot.sepa.engine.gates; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.java_websocket.WebSocket; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.Notification; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; +import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; + +public class WebsocketGate extends Gate { + protected static final Logger logger = LogManager.getLogger(); + + protected final WebSocket socket; + + public WebsocketGate(WebSocket s,Scheduler scheduler){ + super(scheduler); + this.socket = s; + } + + public void send(String ret) throws SEPAProtocolException { + try{ + socket.send(ret); + } + catch(Exception e){ + logger.warn("Socket: "+socket.hashCode()+" failed to send response: "+ret+" Exception:"+e.getMessage()); + throw new SEPAProtocolException(e); + } + } + + @Override + public void sendResponse(Response response) throws SEPAProtocolException { + super.sendResponse(response); + + // JMX + if (response.isSubscribeResponse()) { + WebsocketBeans.subscribeResponse(); + } else if (response.isUnsubscribeResponse()) { + WebsocketBeans.unsubscribeResponse(); + } else if (response.isError()) { + WebsocketBeans.errorResponse(); + logger.error(response); + } + } + + @Override + public void notifyEvent(Notification notify) throws SEPAProtocolException { + super.notifyEvent(notify); + + WebsocketBeans.notification(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java index 1aaa0359..efea71f2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/EngineBeans.java @@ -78,7 +78,7 @@ public static void resetAll() { QueryProcessorBeans.reset(); UpdateProcessorBeans.reset(); SchedulerBeans.reset(); - SubscribeProcessorBeans.reset(); + SPUManagerBeans.reset(); } public static String getHost() { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SPUManagerBeans.java similarity index 98% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SPUManagerBeans.java index 70f9dc29..a5113d3c 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SubscribeProcessorBeans.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/bean/SPUManagerBeans.java @@ -1,6 +1,6 @@ package it.unibo.arces.wot.sepa.engine.bean; -public class SubscribeProcessorBeans { +public class SPUManagerBeans { private static long requests = 0; private static float minTime = -1; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index 53f8355d..b6db35f5 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -17,13 +17,6 @@ */ package it.unibo.arces.wot.sepa.engine.core; -import java.io.IOException; - -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; @@ -32,8 +25,6 @@ import org.apache.logging.log4j.LogManager; -import com.nimbusds.jose.JOSEException; - import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -41,8 +32,8 @@ import it.unibo.arces.wot.sepa.engine.bean.EngineBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; - +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; +import it.unibo.arces.wot.sepa.engine.dependability.DependabilityMonitor; import it.unibo.arces.wot.sepa.engine.processing.Processor; import it.unibo.arces.wot.sepa.engine.protocol.websocket.WebsocketServer; @@ -61,13 +52,13 @@ */ public class Engine implements EngineMBean { - private static Engine engine; - private static String version = "0.9.5"; + // private final static Engine engine; + private final static String version = "0.9.5"; private EngineProperties properties = null; // Scheduler request queue - //private final SchedulerQueue schedulerQueue = new SchedulerQueue(); - + // private final SchedulerQueue schedulerQueue = new SchedulerQueue(); + // Primitives scheduler/dispatcher private Scheduler scheduler = null; @@ -78,21 +69,17 @@ public class Engine implements EngineMBean { private HttpGate httpGate = null; // SPARQL 1.1 SE Protocol handler - private WebsocketServer wsServer; - private SecureWebsocketServer wssServer; + private WebsocketServer wsServer = null; private HttpsGate httpsGate = null; private int wsShutdownTimeout = 5000; - // Oauth 2.0 Authorization Server - private AuthorizationManager oauth; - // JKS Credentials private String storeName = "sepa.jks"; private String storePassword = "sepa2017"; private String jwtAlias = "sepakey"; private String jwtPassword = "sepa2017"; private String serverCertificate = "sepacert"; - + // Properties files private String engineJpar = "engine.jpar"; private String endpointJpar = "endpoint.jpar"; @@ -112,12 +99,15 @@ private void setLoggingFileName() { private void printUsage() { System.out.println("Usage:"); - System.out.println("java [JMX] [JVM] [LOG4J] -jar SEPAEngine_X.Y.Z.jar [-help] [-engine=engine.jpar] [-endpoint=endpoint.jpar] [JKS OPTIONS]"); + System.out.println( + "java [JMX] [JVM] [LOG4J] -jar SEPAEngine_X.Y.Z.jar [-help] [-engine=engine.jpar] [-endpoint=endpoint.jpar] [JKS OPTIONS]"); System.out.println("Options: "); - System.out.println("-engine : can be used to specify the JSON configuration parameters for the engine (default: engine.jpar)"); - System.out.println("-endpoint : can be used to specify the JSON configuration parameters for the endpoint (default: endpoint.jpar)"); + System.out.println( + "-engine : can be used to specify the JSON configuration parameters for the engine (default: engine.jpar)"); + System.out.println( + "-endpoint : can be used to specify the JSON configuration parameters for the endpoint (default: endpoint.jpar)"); System.out.println("-help : to print this help"); - + System.out.println(""); System.out.println("JMX:"); System.out.println("-Dcom.sun.management.config.file=jmx.properties : to enable JMX remote managment"); @@ -174,13 +164,7 @@ private void parsingArgument(String[] args) throws PatternSyntaxException { } } - public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException { - // Set logging file name with the current timestamp YYYYMMDD_HH_MM_SS - setLoggingFileName(); - - // Command arguments - parsingArgument(args); - + public Engine(String[] args) { System.out .println("##########################################################################################"); System.out @@ -201,180 +185,157 @@ public Engine(String[] args) throws SEPASecurityException, SEPAProtocolException .println("# GITHUB: https://github.com/arces-wot/sepa #"); System.out .println("# WEB: http://site.unibo.it/wot #"); - System.out.println("# WIKI: https://github.com/arces-wot/SEPA/wiki #"); + System.out + .println("# WIKI: https://github.com/arces-wot/SEPA/wiki #"); System.out .println("##########################################################################################"); + // Set logging file name with the current timestamp YYYYMMDD_HH_MM_SS + setLoggingFileName(); + + // Command arguments + parsingArgument(args); + // Beans SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); EngineBeans.setVersion(version); - - // Initialize SPARQL 1.1 SE processing service properties + new DependabilityMonitor(); + try { + // Initialize SPARQL 1.1 SE processing service properties properties = new EngineProperties(engineJpar); - } catch (SEPAPropertiesException e) { - System.err.println(e.getLocalizedMessage()); - System.exit(1); - } - EngineBeans.setEngineProperties(properties); - - SPARQL11Properties endpointProperties = null; - try { - endpointProperties = new SPARQL11Properties(endpointJpar ); - } catch (SEPAPropertiesException e2) {} - - // OAUTH 2.0 Authorization Manager - if (properties.isSecure()) { - try { - oauth = new AuthorizationManager(storeName, storePassword, jwtAlias, jwtPassword, serverCertificate); - } catch (UnrecoverableKeyException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException - | CertificateException | IOException | JOSEException e1) { - System.err.println(e1.getLocalizedMessage()); - System.exit(1); + EngineBeans.setEngineProperties(properties); + + SPARQL11Properties endpointProperties = null; + endpointProperties = new SPARQL11Properties(endpointJpar); + + // OAUTH 2.0 Authorization Manager + if (properties.isSecure()) { + Dependability.enableSecurity(storeName, storePassword, jwtAlias, jwtPassword, serverCertificate); + } + + // SPARQL 1.1 SE request scheduler + scheduler = new Scheduler(properties); + + // SEPA Processor + processor = new Processor(endpointProperties, properties, scheduler); + Dependability.setProcessor(processor); + + // SPARQL protocol service + int port = endpointProperties.getDefaultPort(); + String portS = ""; + if (port != -1) + portS = String.format(":%d", port); + + String queryMethod = ""; + switch (endpointProperties.getQueryMethod()) { + case POST: + queryMethod = " (Method: POST)"; + break; + case GET: + queryMethod = " (Method: GET)"; + break; + case URL_ENCODED_POST: + queryMethod = " (Method: URL ENCODED POST)"; + break; } - } - // SPARQL 1.1 SE request scheduler - scheduler = new Scheduler(properties); - scheduler.start(); + String updateMethod = ""; + switch (endpointProperties.getUpdateMethod()) { + case POST: + updateMethod = " (Method: POST)"; + break; + case GET: + updateMethod = " (Method: GET)"; + break; + case URL_ENCODED_POST: + updateMethod = " (Method: URL ENCODED POST)"; + break; + } - // SEPA Processor - try { - processor = new Processor(endpointProperties, properties, scheduler.getSchedulerQueue()); - } catch (SEPAProtocolException e1) { - System.err.println(e1.getMessage()); - System.exit(1); - } - processor.start(); - - // SPARQL protocol service - int port = endpointProperties.getDefaultPort(); - String portS = ""; - if (port != -1) - portS = String.format(":%d", port); - - String queryMethod = ""; - switch (endpointProperties.getQueryMethod()) { - case POST: - queryMethod = " (Method: POST)"; - break; - case GET: - queryMethod = " (Method: GET)"; - break; - case URL_ENCODED_POST: - queryMethod = " (Method: URL ENCODED POST)"; - break; - } + System.out.println("SPARQL 1.1 endpoint"); + System.out.println("----------------------"); + System.out.println("SPARQL 1.1 Query | http://" + endpointProperties.getDefaultHost() + portS + + endpointProperties.getDefaultQueryPath() + queryMethod); + System.out.println("SPARQL 1.1 Update | http://" + endpointProperties.getDefaultHost() + portS + + endpointProperties.getUpdatePath() + updateMethod); + System.out.println("----------------------"); + System.out.println(""); - String updateMethod = ""; - switch (endpointProperties.getUpdateMethod()) { - case POST: - updateMethod = " (Method: POST)"; - break; - case GET: - updateMethod = " (Method: GET)"; - break; - case URL_ENCODED_POST: - updateMethod = " (Method: URL ENCODED POST)"; - break; - } + // SPARQL 1.1 protocol gates + System.out.println("SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); + System.out.println("----------------------"); - System.out.println("SPARQL 1.1 endpoint"); - System.out.println("----------------------"); - System.out.println("SPARQL 1.1 Query | http://" + endpointProperties.getDefaultHost() + portS - + endpointProperties.getDefaultQueryPath() + queryMethod); - System.out.println("SPARQL 1.1 Update | http://" + endpointProperties.getDefaultHost() + portS - + endpointProperties.getUpdatePath() + updateMethod); - System.out.println("----------------------"); - System.out.println(""); - - // Protocol gates - System.out.println("SPARQL 1.1 Protocol (https://www.w3.org/TR/sparql11-protocol/)"); - System.out.println("----------------------"); - try { if (!properties.isSecure()) httpGate = new HttpGate(properties, scheduler); else - httpsGate = new HttpsGate(properties, scheduler, oauth); - } catch (SEPAProtocolException e) { - System.err.println(e.getMessage()); - System.exit(1); - } - - // Protocol gates - System.out.println("----------------------"); - System.out.println(""); - System.out.println("SPARQL 1.1 SE Protocol (https://mml.arces.unibo.it/TR/sparql11-se-protocol/)"); - System.out.println("----------------------"); - - if (!properties.isSecure()) { - wsServer = new WebsocketServer(properties.getWsPort(), properties.getSubscribePath(), scheduler); + httpsGate = new HttpsGate(properties, scheduler); + + // SPARQL 1.1 SE protocol gates + System.out.println("----------------------"); + System.out.println(""); + System.out.println("SPARQL 1.1 SE Protocol (https://mml.arces.unibo.it/TR/sparql11-se-protocol/)"); + System.out.println("----------------------"); + + if (!properties.isSecure()) { + wsServer = new WebsocketServer(properties.getWsPort(), properties.getSubscribePath(), scheduler); + } else { + wsServer = new SecureWebsocketServer(properties.getWssPort(), + properties.getSecurePath() + properties.getSubscribePath(), scheduler); + } + + // Start all + scheduler.start(); + processor.start(); wsServer.start(); + synchronized (wsServer) { - try { - wsServer.wait(5000); - } catch (InterruptedException e) { - throw new SEPAProtocolException(e); - } + wsServer.wait(5000); } - } else { - wssServer = new SecureWebsocketServer(properties.getWssPort(), - properties.getSecurePath() + properties.getSubscribePath(), scheduler, oauth); - - wssServer.start(); - synchronized (wssServer) { - try { - wssServer.wait(5000); - } catch (InterruptedException e) { - throw new SEPAProtocolException(e); - } - } - } - System.out.println("----------------------"); + System.out.println("----------------------"); + + // Welcome message + System.out.println(""); + System.out.println( + "*****************************************************************************************"); + System.out.println("* SEPA Broker Ver " + version + + " is up and running *"); + System.out.println( + "* Let Things Talk! *"); + System.out.println( + "*****************************************************************************************"); + + } catch (SEPAPropertiesException | SEPASecurityException + | IllegalArgumentException | SEPAProtocolException | InterruptedException e) { + System.err.println(e.getMessage()); + System.exit(1); + } - // Welcome message - System.out.println(""); - System.out.println("*****************************************************************************************"); - System.out.println( - "* SEPA Broker Ver " + version + " is up and running *"); - System.out.println("* Let Things Talk! *"); - System.out.println("*****************************************************************************************"); } public static void main(String[] args) throws SEPASecurityException, SEPAProtocolException { - engine = new Engine(args); - // Attach CTRL+C hook - Runtime.getRuntime().addShutdownHook(new EngineShutdownHook(engine)); + Runtime.getRuntime().addShutdownHook(new EngineShutdownHook(new Engine(args))); } - public void shutdown() { + public void shutdown() throws InterruptedException { System.out.println("Stopping..."); - if (!properties.isSecure()) { + if (httpGate != null) { System.out.println("Stopping HTTP gate..."); httpGate.shutdown(); - - try { - System.out.println("Stopping WS gate..."); - wsServer.stop(wsShutdownTimeout); - } catch (InterruptedException e) { - System.err.println(e.getMessage()); - } } - - if (properties.isSecure()) { + + if (httpsGate != null) { System.out.println("Stopping HTTPS gate..."); httpsGate.shutdown(); - - try { - System.out.println("Stopping WSS gate..."); - wssServer.stop(wsShutdownTimeout); - } catch (InterruptedException e) { - System.err.println(e.getMessage()); - } + } + + if (wsServer != null) { + System.out.println("Stopping WebSocket gate..."); + wsServer.stop(wsShutdownTimeout); } System.out.println("Stopping Processor..."); @@ -397,17 +358,17 @@ public void resetAll() { public String getVersion() { return EngineBeans.getVersion(); } - + @Override public String getQueryPath() { return EngineBeans.getQueryPath(); } - + @Override public String getUpdatePath() { return EngineBeans.getUpdatePath(); } - + @Override public String getSubscribePath() { return EngineBeans.getSubscribePath(); @@ -417,37 +378,37 @@ public String getSubscribePath() { public String getSecurePath() { return EngineBeans.getSecurePath(); } - + @Override public String getRegisterPath() { return EngineBeans.getRegisterPath(); } - + @Override public String getTokenRequestPath() { return EngineBeans.getTokenRequestPath(); } - + @Override public int getHttpPort() { return EngineBeans.getHttpPort(); } - + @Override public int getHttpsPort() { return EngineBeans.getHttpsPort(); } - + @Override public int getWsPort() { return EngineBeans.getWsPort(); } - + @Override public int getWssPort() { return EngineBeans.getWssPort(); } - + @Override public boolean getSecure() { return EngineBeans.getSecure(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java index 8740c129..318a514a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineProperties.java @@ -171,119 +171,136 @@ protected void validate() throws SEPAPropertiesException { properties.get("scheduler").getAsJsonObject().get("queueSize").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("scheduler-queueSize is missing")); + logger.error("scheduler-queueSize is missing"); + throw new SEPAPropertiesException("scheduler-queueSize is missing"); } try { properties.get("scheduler").getAsJsonObject().get("timeout").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("scheduler-timeout is missing")); + logger.error("scheduler-timeout is missing"); + throw new SEPAPropertiesException("scheduler-timeout is missing"); } try { properties.get("processor").getAsJsonObject().get("updateTimeout").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("processor-updateTimeout is missing")); + logger.error("processor-updateTimeout is missing"); + throw new SEPAPropertiesException("processor-updateTimeout is missing"); } try { properties.get("processor").getAsJsonObject().get("queryTimeout").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("processor-queryTimeout is missing")); + logger.error("processor-queryTimeout is missing"); + throw new SEPAPropertiesException("processor-queryTimeout is missing"); } try { properties.get("processor").getAsJsonObject().get("maxConcurrentRequests").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("processor-maxConcurrentRequests is missing")); + logger.error("processor-maxConcurrentRequests is missing"); + throw new SEPAPropertiesException("processor-maxConcurrentRequests is missing"); } try { properties.get("spu").getAsJsonObject().get("timeout").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("spu-timeout is missing")); + logger.error("spu-timeout is missing"); + throw new SEPAPropertiesException("spu-timeout is missing"); } try { properties.get("gates").getAsJsonObject().get("secure").getAsBoolean(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-secure is missing")); + logger.error("gates-secure is missing"); + throw new SEPAPropertiesException("gates-secure is missing"); } try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("http").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-ports-http is missing")); + logger.error("gates-ports-http is missing"); + throw new SEPAPropertiesException("gates-ports-http is missing"); } try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("https").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-ports-https is missing")); + logger.error("gates-ports-https is missing"); + throw new SEPAPropertiesException("gates-ports-https is missing"); } try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("ws").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-ports-ws is missing")); + logger.error("gates-ports-ws is missing"); + throw new SEPAPropertiesException("gates-ports-ws is missing"); } try { properties.get("gates").getAsJsonObject().get("ports").getAsJsonObject().get("wss").getAsInt(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-ports-wss is missing")); + logger.error("gates-ports-wss is missing"); + throw new SEPAPropertiesException("gates-ports-wss is missing"); } try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("securePath").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-paths-securePath is missing")); + logger.error("gates-paths-securePath is missing"); + throw new SEPAPropertiesException("gates-paths-securePath is missing"); } try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("update").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-paths-update is missing")); + logger.error("gates-paths-update is missing"); + throw new SEPAPropertiesException("gates-paths-update is missing"); } try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("query").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-paths-query is missing")); + logger.error("gates-paths-query is missing"); + throw new SEPAPropertiesException("gates-paths-query is missing"); } try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("subscribe").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-paths-subscribe is missing")); + logger.error("gates-paths-subscribe is missing"); + throw new SEPAPropertiesException("gates-paths-subscribe is missing"); } try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("register").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-paths-register is missing")); + logger.error("gates-paths-register is missing"); + throw new SEPAPropertiesException("gates-paths-register is missing"); } try { properties.get("gates").getAsJsonObject().get("paths").getAsJsonObject().get("tokenRequest").getAsString(); } catch (Exception e) { - throw new SEPAPropertiesException(new Exception("gates-paths-tokenRequest is missing")); + logger.error("gates-paths-tokenRequest is missing"); + throw new SEPAPropertiesException("gates-paths-tokenRequest is missing"); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineShutdownHook.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineShutdownHook.java index ae386447..0317f42f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineShutdownHook.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/EngineShutdownHook.java @@ -1,6 +1,11 @@ package it.unibo.arces.wot.sepa.engine.core; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class EngineShutdownHook extends Thread { + private static final Logger logger = LogManager.getLogger(); + private Engine engine; public EngineShutdownHook(Engine engine) { @@ -9,6 +14,10 @@ public EngineShutdownHook(Engine engine) { } public void run() { - engine.shutdown(); + try { + engine.shutdown(); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java index 6a46b1f1..92d7c36f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/ResponseHandler.java @@ -1,20 +1,8 @@ package it.unibo.arces.wot.sepa.engine.core; -import java.util.UUID; - import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.response.Response; -public abstract class ResponseHandler { - private final UUID uuid; - - public ResponseHandler() { - uuid = UUID.randomUUID(); - } - +public interface ResponseHandler { public abstract void sendResponse(Response response) throws SEPAProtocolException; - - public final UUID getUUID() { - return uuid; - } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java index e5046de4..c86ba3a9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java @@ -38,15 +38,10 @@ import javax.net.ssl.SSLContext; -//import org.apache.http.Header; -//import org.apache.http.HttpRequest; - import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -//import org.glassfish.grizzly.ssl.SSLEngineConfigurator; - import com.google.gson.JsonElement; import com.google.gson.JsonParser; @@ -63,7 +58,6 @@ import com.nimbusds.jose.proc.BadJOSEException; import com.nimbusds.jose.proc.JWSKeySelector; import com.nimbusds.jose.proc.JWSVerificationKeySelector; -import com.nimbusds.jose.proc.SecurityContext; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; @@ -76,48 +70,35 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.engine.bean.AuthorizationManagerBeans; -import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -public class AuthorizationManager implements AuthorizationManagerMBean { - private static final Logger logger = LogManager.getLogger("AuthorizationManager"); +class AuthorizationManager { + private static final Logger logger = LogManager.getLogger(); //TODO: CLIENTS DB to be made persistent //IDENTITY ==> ID - private HashMap clients = new HashMap(); + private static final HashMap clients = new HashMap(); //TODO: CREDENTIALS DB to be made persistent //ID ==> Secret - private HashMap credentials = new HashMap(); + private static final HashMap credentials = new HashMap(); //TODO: TOKENS DB to be made persistent //ID ==> JWTClaimsSet - private HashMap clientClaims = new HashMap(); + private static final HashMap clientClaims = new HashMap(); //************************* //JWT signing and verifying //************************* - private JWSSigner signer; - private RSASSAVerifier verifier; - private JsonElement jwkPublicKey; - private ConfigurableJWTProcessor jwtProcessor; - private SEPASecurityContext context = new SEPASecurityContext(); - private SEPASecurityManager sManager; - - /** - Security context. Provides additional information necessary for processing a JOSE object. - Example context information: - - Identifier of the message producer (e.g. OpenID Connect issuer) to retrieve its public key to verify the JWS signature. - Indicator whether the message was received over a secure channel (e.g. TLS/SSL) which is essential for processing unsecured (plain) JOSE objects. - */ - private class SEPASecurityContext implements SecurityContext { - - } + private static JWSSigner signer; + private static RSASSAVerifier verifier; + private static JsonElement jwkPublicKey; + private static ConfigurableJWTProcessor jwtProcessor; + private static SEPASecurityManager sManager; - private void securityCheck(String identity) { + private static void securityCheck(String identity) { logger.debug("*** Security check ***"); //Add identity - addAuthorizedIdentity(identity); + AuthorizationManagerBeans.getAuthorizedIdentities().put(identity, true); //Register logger.debug("Register: "+identity); @@ -149,32 +130,11 @@ private void securityCheck(String identity) { logger.debug("**********************"); System.out.println(""); - //Add identity - removeAuthorizedIdentity(identity); + //Remove identity + AuthorizationManagerBeans.getAuthorizedIdentities().remove(identity); } - -// /** -// * Gets the RSA Key from the keystore. -// * -// * @param keyAlias -// * the key alias -// * @param keyPwd -// * the key password -// * @return the RSAKey -// * @throws JOSEException -// * @throws KeyStoreException -// * -// * @see RSAKey -// */ -// private RSAKey getJWK(String keyAlias, String keyPwd) throws KeyStoreException, JOSEException { -// RSAKey jwk = null; -// -// jwk = RSAKey.load(sManager.getKeyStore(), keyAlias, keyPwd.toCharArray()); -// -// return jwk; -// } - private boolean init(KeyStore keyStore,String keyAlias,String keyPwd) throws KeyStoreException, JOSEException{ + private static void initStore(KeyStore keyStore,String keyAlias,String keyPwd) throws KeyStoreException, JOSEException{ // Load the key from the key store RSAKey jwk = RSAKey.load(keyStore, keyAlias, keyPwd.toCharArray()); @@ -202,15 +162,11 @@ private boolean init(KeyStore keyStore,String keyAlias,String keyPwd) throws Key JWSAlgorithm expectedJWSAlg = JWSAlgorithm.RS256; JWSKeySelector keySelector = new JWSVerificationKeySelector(expectedJWSAlg, keySource); jwtProcessor.setJWSKeySelector(keySelector); - - return true; } - public AuthorizationManager(String keystoreFileName,String keystorePwd,String keyAlias,String keyPwd,String certificate) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, JOSEException, SEPASecurityException { - SEPABeans.registerMBean("SEPA:type=AuthorizationManager",this); - + public static void init(String keystoreFileName,String keystorePwd,String keyAlias,String keyPwd,String certificate) throws UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, JOSEException, SEPASecurityException { sManager = new SEPASecurityManager(keystoreFileName, keystorePwd,keyPwd); - init(sManager.getKeyStore(),keyAlias, keyPwd); + initStore(sManager.getKeyStore(),keyAlias, keyPwd); securityCheck(UUID.randomUUID().toString()); } @@ -241,28 +197,8 @@ public AuthorizationManager(String keystoreFileName,String keystorePwd,String ke
    */ -// private synchronized Response authorizeRequest(HttpRequest request) { -// // Extract Bearer authorization -// Header[] bearer = request.getHeaders("Authorization"); -// -// if (bearer.length != 1) { -// logger.error("Authorization header is missing or multiple"); -// return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_request","Authorization header must be a single one"); -// } -// if (!bearer[0].getValue().startsWith("Bearer ")) { -// logger.error("Authorization must be \"Bearer JWT\""); -// return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_request","Authorization header must be \"Bearer JWT\""); -// } -// -// // ****************** -// // JWT validation -// // ****************** -// String jwt = bearer[0].getValue().split(" ")[1]; -// -// return validateToken(jwt); -// } - private boolean authorizeIdentity(String id) { + private static boolean authorizeIdentity(String id) { logger.debug("Authorize identity:"+id); //TODO: WARNING! TO BE REMOVED IN PRODUCTION. ONLY FOR TESTING. @@ -312,7 +248,7 @@ private boolean authorizeIdentity(String id) { * * @param identity the client identity to be registered * */ - public synchronized Response register(String identity) { + public static synchronized Response register(String identity) { logger.info("REGISTER: "+identity); //Check if entity is authorized to request credentials @@ -379,7 +315,7 @@ public synchronized Response register(String identity) { */ - public synchronized Response getToken(String encodedCredentials) { + public static synchronized Response getToken(String encodedCredentials) { logger.debug("Get token"); //Decode credentials @@ -579,7 +515,7 @@ public synchronized Response getToken(String encodedCredentials) { @param accessToken the JWT token to be validate according to points 4-9 */ - public synchronized Response validateToken(String accessToken) { + public static synchronized Response validateToken(String accessToken) { logger.trace("Validate token"); //Parse and verify the token @@ -604,7 +540,7 @@ public synchronized Response validateToken(String accessToken) { // Process the token (validate) JWTClaimsSet claimsSet = null; try { - claimsSet = jwtProcessor.process(accessToken, context); + claimsSet = jwtProcessor.process(accessToken, new SEPASecurityContext()); } catch (ParseException e) { logger.error(e.getMessage()); return new ErrorResponse(HttpStatus.SC_BAD_REQUEST,"invalid_grant","ParseException: "+e.getMessage()); @@ -625,74 +561,8 @@ public synchronized Response validateToken(String accessToken) { return new JWTResponse(accessToken,"bearer",claimsSet.getExpirationTime().getTime()-new Date().getTime()); } - - @Override - public long getTokenExpiringPeriod() { - return AuthorizationManagerBeans.getTokenExpiringPeriod(); - } - - - @Override - public void setTokenExpiringPeriod(long period) { - AuthorizationManagerBeans.setTokenExpiringPeriod(period); - } - - @Override - public void addAuthorizedIdentity(String id) { - AuthorizationManagerBeans.getAuthorizedIdentities().put(id, true); - } - - @Override - public void removeAuthorizedIdentity(String id) { - AuthorizationManagerBeans.getAuthorizedIdentities().remove(id); - } - - @Override - public HashMap getAuthorizedIdentities() { - return AuthorizationManagerBeans.getAuthorizedIdentities(); - } - - @Override - public String getIssuer() { - return AuthorizationManagerBeans.getIssuer(); - } - - @Override - public void setIssuer(String issuer) { - AuthorizationManagerBeans.setIssuer(issuer); - } - - @Override - public String getHttpsAudience() { - return AuthorizationManagerBeans.getHttpsAudience(); - } - - @Override - public void setHttpsAudience(String audience) { - AuthorizationManagerBeans.setHttpsAudience(audience); - } - - @Override - public String getWssAudience() { - return AuthorizationManagerBeans.getWssAudience(); - } - - @Override - public void setWssAudience(String audience) { - AuthorizationManagerBeans.setWssAudience(audience); - } - - @Override - public String getSubject() { - return AuthorizationManagerBeans.getSubject(); - } - - @Override - public void setSubject(String sub) { - AuthorizationManagerBeans.setSubject(sub); - } - public SSLContext getSSLContext() throws SEPASecurityException { + public static SSLContext getSSLContext() throws SEPASecurityException { return sManager.getSSLContext(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java index 081ab400..bf34992a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/CORSManager.java @@ -26,10 +26,10 @@ /** * The Class CORSManager. */ -public class CORSManager { +class CORSManager { /** The logger. */ - protected static Logger logger = LogManager.getLogger("CORSManager"); + protected static Logger logger = LogManager.getLogger(); /** * Process a CORS (Cross-Origin Resource Sharing) pre-flight request.
    diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Dependability.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Dependability.java new file mode 100644 index 00000000..b3d76077 --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/Dependability.java @@ -0,0 +1,88 @@ +package it.unibo.arces.wot.sepa.engine.dependability; + +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; + +import javax.net.ssl.SSLContext; + +import org.apache.http.nio.protocol.HttpAsyncExchange; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import com.nimbusds.jose.JOSEException; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.engine.processing.Processor; + +public class Dependability { + protected static Logger logger = LogManager.getLogger(); + + private static boolean isSecure = false; + + public static boolean isSecure() { + return isSecure; + } + + public static void enableSecurity(String keystoreFileName,String keystorePwd,String keyAlias,String keyPwd,String certificate) throws SEPASecurityException { + try { + AuthorizationManager.init(keystoreFileName, keystorePwd, keyAlias, keyPwd, certificate); + } catch (UnrecoverableKeyException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException + | CertificateException | IOException | JOSEException | SEPASecurityException e) { + logger.error(e.getMessage()); + throw new SEPASecurityException(e); + } + isSecure = true; + } + + public static void setProcessor(Processor p) { + SubscriptionManager.setProcessor(p); + } + + public static Response validateToken(String jwt) { + return AuthorizationManager.validateToken(jwt); + } + + public static void onCloseGate(String gid) { + SubscriptionManager.onClose(gid); + } + + public static void onGateError(String gid, Exception e) { + SubscriptionManager.onError(gid, e); + } + + public static void onSubscribe(String gid, String sid) { + SubscriptionManager.onSubscribe(gid, sid); + } + + public static void onUnsubscribe(String gid, String sid) { + SubscriptionManager.onUnsubscribe(gid, sid); + } + + public static SSLContext getSSLContext() throws SEPASecurityException { + return AuthorizationManager.getSSLContext(); + } + + public static Response getToken(String encodedCredentials) { + return AuthorizationManager.getToken(encodedCredentials); + } + + public static Response register(String identity) { + return AuthorizationManager.register(identity); + } + + public static boolean processCORSRequest(HttpAsyncExchange exchange) { + return CORSManager.processCORSRequest(exchange); + } + + public static boolean isPreFlightRequest(HttpAsyncExchange exchange) { + return CORSManager.isPreFlightRequest(exchange); + } + + + +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java deleted file mode 100644 index 73156cde..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityManager.java +++ /dev/null @@ -1,70 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.dependability; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.UUID; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerQueue; - -public class DependabilityManager { - private static final Logger logger = LogManager.getLogger(); - - // Active subscriptions - private static final HashMap> subscriptions = new HashMap>(); - - // Scheduler queue - private final SchedulerQueue schedulerQueue; - - public DependabilityManager(SchedulerQueue schedulerQueue) { - this.schedulerQueue = schedulerQueue; - } - - public synchronized void onSubscribe(UUID uuid, String spuid) { - if (uuid == null || spuid == null) { - logger.error("Some values are null. UUID: "+uuid+" SPUID: "+spuid); - return; - } - - if (!subscriptions.containsKey(uuid)) - subscriptions.put(uuid, new ArrayList()); - subscriptions.get(uuid).add(spuid); - - logger.debug("@onSubscribe Subscriptions: " + subscriptions.size()+" Handlers (" + uuid + "): " + subscriptions.get(uuid).size()); - } - - public synchronized void onUnsubscribe(UUID uuid, String spuid) { - logger.debug("@onUnsubscribe: " + uuid + " SPUID: " + spuid); - - subscriptions.get(uuid).remove(spuid); - if (subscriptions.get(uuid).isEmpty()) - subscriptions.remove(uuid); - - logger.debug("Active subscriptions: "+subscriptions.size()); - } - - public synchronized void onBrokenSubscription(UUID uuid) { - if (!subscriptions.containsKey(uuid)) { - logger.warn("Broken socket not registered: "+uuid); - return; - } - - logger.debug(String.format("@onBrokenSocket: " + uuid +" with active subscriptions: %d", subscriptions.get(uuid).size())+" (sockets opened: "+subscriptions.size()+")"); - - // Kill all SPUs - for (String spuid : subscriptions.get(uuid)) { - logger.trace("Schedule request to kill SPU: " + spuid); - schedulerQueue.killSpuid(spuid); - } - - // Remove subscriptions - subscriptions.remove(uuid); - } - - public void onError(UUID uuid, ErrorResponse error) { - logger.error("Subscription:" + uuid + " error:" + error); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitor.java new file mode 100644 index 00000000..009f63da --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitor.java @@ -0,0 +1,88 @@ +package it.unibo.arces.wot.sepa.engine.dependability; + +import java.util.HashMap; + +import it.unibo.arces.wot.sepa.engine.bean.AuthorizationManagerBeans; +import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; + +public class DependabilityMonitor implements DependabilityMonitorMBean { + + public DependabilityMonitor() { + SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); + } + + @Override + public long getTokenExpiringPeriod() { + return AuthorizationManagerBeans.getTokenExpiringPeriod(); + } + + @Override + public void setTokenExpiringPeriod(long period) { + AuthorizationManagerBeans.setTokenExpiringPeriod(period); + } + + @Override + public void addAuthorizedIdentity(String id) { + AuthorizationManagerBeans.getAuthorizedIdentities().put(id, true); + } + + @Override + public void removeAuthorizedIdentity(String id) { + AuthorizationManagerBeans.getAuthorizedIdentities().remove(id); + } + + @Override + public HashMap getAuthorizedIdentities() { + return AuthorizationManagerBeans.getAuthorizedIdentities(); + } + + @Override + public String getIssuer() { + return AuthorizationManagerBeans.getIssuer(); + } + + @Override + public void setIssuer(String issuer) { + AuthorizationManagerBeans.setIssuer(issuer); + } + + @Override + public String getHttpsAudience() { + return AuthorizationManagerBeans.getHttpsAudience(); + } + + @Override + public void setHttpsAudience(String audience) { + AuthorizationManagerBeans.setHttpsAudience(audience); + } + + @Override + public String getWssAudience() { + return AuthorizationManagerBeans.getWssAudience(); + } + + @Override + public void setWssAudience(String audience) { + AuthorizationManagerBeans.setWssAudience(audience); + } + + @Override + public String getSubject() { + return AuthorizationManagerBeans.getSubject(); + } + + @Override + public void setSubject(String sub) { + AuthorizationManagerBeans.setSubject(sub); + } + + @Override + public long getNumberOfGates() { + return SubscriptionManager.getNumberOfGates(); + } + + @Override + public boolean getSecure() { + return Dependability.isSecure(); + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManagerMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitorMBean.java similarity index 93% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManagerMBean.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitorMBean.java index ae4cd6ce..9b1bedd9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManagerMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/DependabilityMonitorMBean.java @@ -20,7 +20,7 @@ import java.util.HashMap; -public interface AuthorizationManagerMBean { +public interface DependabilityMonitorMBean { void addAuthorizedIdentity(String id); void removeAuthorizedIdentity(String id); @@ -46,4 +46,8 @@ public interface AuthorizationManagerMBean { String getSubject(); void setSubject(String sub); + + long getNumberOfGates(); + + boolean getSecure(); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SEPASecurityContext.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SEPASecurityContext.java new file mode 100644 index 00000000..fe0454dc --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SEPASecurityContext.java @@ -0,0 +1,14 @@ +package it.unibo.arces.wot.sepa.engine.dependability; + +import com.nimbusds.jose.proc.SecurityContext; + +/** +Security context. Provides additional information necessary for processing a JOSE object. +Example context information: + +Identifier of the message producer (e.g. OpenID Connect issuer) to retrieve its public key to verify the JWS signature. +Indicator whether the message was received over a secure channel (e.g. TLS/SSL) which is essential for processing unsecured (plain) JOSE objects. +*/ + class SEPASecurityContext implements SecurityContext { + +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SubscriptionManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SubscriptionManager.java new file mode 100644 index 00000000..321340cf --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/SubscriptionManager.java @@ -0,0 +1,102 @@ +package it.unibo.arces.wot.sepa.engine.dependability; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.engine.processing.Processor; + +class SubscriptionManager { + private static final Logger logger = LogManager.getLogger(); + + // Active gates + private static final HashMap> gates = new HashMap>(); + + private static Processor processor = null; + + public static void setProcessor(Processor p) { + processor = p; + } + + public static synchronized void onSubscribe(String gid, String sid) { + if (gid == null) { + logger.error("@onSubscribe GID is null"); + return; + } + if (sid == null) { + logger.error("@onSubscribe SID is null"); + return; + } + + // Gate exists? + if (!gates.containsKey(gid)) { + // Create a new gate entry + logger.debug("@onSubscribe create new gate: " + gid); + gates.put(gid, new ArrayList()); + } + + // Add subscription + gates.get(gid).add(sid); + + logger.trace("ADDED " + gid + " " + sid + " " + gates.size() + " " + gates.get(gid).size()); + } + + public static synchronized void onUnsubscribe(String gid, String sid) { + if (gid == null) { + logger.error("@onUnsubscribe GID is null"); + return; + } + if (sid == null) { + logger.error("@onUnsubscribe SID is null"); + return; + } + + logger.trace("REMOVE " + gid + " " + sid + " " + gates.size() + " " + gates.get(gid).size()); + + // Remove subscription + gates.get(gid).remove(sid); + } + + public static synchronized void onClose(String gid) { + if (gid == null) { + logger.error("@onClose GID is null"); + return; + } + if (processor == null) { + logger.error("@onClose processor is null"); + return; + } + + // Gate exists? + if (!gates.containsKey(gid)) { + logger.warn("NOT_FOUND " + gid + " " + "---" + " " + gates.size() + " " + "-1"); + return; + } + + logger.trace("CLOSE " + gid + " --- " + gates.size() + " " + gates.get(gid).size()); + + // Kill all active subscriptions + for (String sid : gates.get(gid)) { + processor.killSubscription(sid, gid); + } + + // Remove gate + gates.remove(gid); + } + + public static synchronized void onError(String gid, Exception e) { + if (gid == null) { + logger.error("@onError GID is null"); + return; + } + + logger.error("@onError GID:" + gid + " Exception:" + e); + } + + public static long getNumberOfGates() { + return gates.size(); + + } +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java index af99067a..6289e7b5 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/Processor.java @@ -23,13 +23,16 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Properties; +import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; import it.unibo.arces.wot.sepa.engine.bean.ProcessorBeans; import it.unibo.arces.wot.sepa.engine.bean.QueryProcessorBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.UpdateProcessorBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPUManager; -import it.unibo.arces.wot.sepa.engine.scheduling.SchedulerQueue; +import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class Processor implements ProcessorMBean { // Processor threads @@ -45,23 +48,24 @@ public class Processor implements ProcessorMBean { private final SPUManager spuManager; // Concurrent endpoint limit - private Semaphore endpointSemaphore = null; + private final Semaphore endpointSemaphore; // Scheduler queue - private final SchedulerQueue queue; + private final Scheduler scheduler; // Running flag private final AtomicBoolean running = new AtomicBoolean(true); public Processor(SPARQL11Properties endpointProperties, EngineProperties properties, - SchedulerQueue queue) throws IllegalArgumentException, SEPAProtocolException { + Scheduler scheduler) throws IllegalArgumentException, SEPAProtocolException { // Number of maximum concurrent requests (supported by the endpoint) int max = properties.getMaxConcurrentRequests(); // TODO: extending at run-time the semaphore max if (max > 0) endpointSemaphore = new Semaphore(max, true); - - this.queue = queue; + else endpointSemaphore = null; + + this.scheduler = scheduler; // Processors queryProcessor = new QueryProcessor(endpointProperties,endpointSemaphore); @@ -94,12 +98,8 @@ public boolean isRunning() { return running.get(); } - public SchedulerQueue getSchedulerQueue() { - return queue; - } - - public SPUManager getSPUManager() { - return spuManager; + public Scheduler getScheduler() { + return scheduler; } public QueryProcessor getQueryProcessor() { @@ -124,6 +124,10 @@ public void interrupt() { updateProcessingThread.interrupt(); } + public Response subscribe(InternalSubscribeRequest request) { + return spuManager.subscribe(request); + } + public boolean isUpdateReilable() { return UpdateProcessorBeans.getReilable(); } @@ -162,4 +166,16 @@ public String getEndpointQueryMethod() { public int getMaxConcurrentRequests() { return ProcessorBeans.getMaxConcurrentRequests(); } + + public void killSubscription(String sid, String gid) { + spuManager.killSubscription(sid, gid); + } + + public Response unsubscribe(String sid, String gid) { + return spuManager.unsubscribe(sid, gid); + } + + public void process(UpdateResponse update) { + spuManager.process(update); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java index d80a7c05..c916a74f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/QueryProcessingThread.java @@ -16,7 +16,7 @@ public void run() { while(processor.isRunning()) { ScheduledRequest request; try { - request = processor.getSchedulerQueue().waitQueryRequest(); + request = processor.getScheduler().waitQueryRequest(); } catch (InterruptedException e) { return; } @@ -24,7 +24,7 @@ public void run() { InternalQueryRequest query = (InternalQueryRequest) request.getRequest(); Response ret = processor.getQueryProcessor().process(query); - processor.getSchedulerQueue().addResponse(request.getToken(),ret); + processor.getScheduler().addResponse(request.getToken(),ret); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java index 7308eacc..16ebc0e2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThread.java @@ -1,309 +1,48 @@ package it.unibo.arces.wot.sepa.engine.processing; -import java.util.ArrayList; -import java.util.HashMap; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; -import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; -import it.unibo.arces.wot.sepa.engine.core.EventHandler; -import it.unibo.arces.wot.sepa.engine.processing.subscriptions.SPU; import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; -class SubscribeProcessingThread extends Thread implements SubscribeProcessingThreadMBean, EventHandler { +class SubscribeProcessingThread extends Thread { private static final Logger logger = LogManager.getLogger(); private final Processor processor; - // Maps - private static final HashMap> activeSpus = new HashMap>(); - private static final HashMap spuids = new HashMap(); - private static final HashMap subscribers = new HashMap(); - private static final HashMap sequenceNumbers = new HashMap(); - - // Broken SPUs disposer - private final Thread killer; - public SubscribeProcessingThread(Processor processor) { this.processor = processor; setName("SEPA-Subscribe-Processor"); - - killer = new Thread() { - public void run() { - while (processor.isRunning()) { - try { - String spuid = processor.getSchedulerQueue().waitSpuid2Kill(); -// synchronized (activeSpus) { -// unregisterHandler(spuids.get(spuid), spuid); -// } - unsubscribe(spuid); - } catch (InterruptedException e) { - return; - } - } - } - }; - killer.setName("SEPA-SPU-Killer"); - - SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); - } - - @Override - public void start() { - killer.start(); - super.start(); } - + public void run() { while (processor.isRunning()) { try { // Wait request... - ScheduledRequest request = processor.getSchedulerQueue().waitSubscribeUnsubscribeRequest(); - logger.trace(">> "+request); + ScheduledRequest request = processor.getScheduler().waitSubscribeUnsubscribeRequest(); + logger.debug(">> " + request); // Process request Response response = null; - if (request.isSubscribeRequest()) - response = subscribe((InternalSubscribeRequest) request.getRequest()); - else if (request.isUnsubscribeRequest()) - response = unsubscribe(((InternalUnsubscribeRequest) request.getRequest()).getSpuid()); + if (request.isSubscribeRequest()) { + response = processor.subscribe((InternalSubscribeRequest) request.getRequest()); + }else if (request.isUnsubscribeRequest()) { + String sid = ((InternalUnsubscribeRequest) request.getRequest()).getSID(); + String gid = ((InternalUnsubscribeRequest) request.getRequest()).getGID(); + response = processor.unsubscribe(sid,gid); + } + logger.debug("<< " + response); - logger.trace("<< "+response); - // Send back response - processor.getSchedulerQueue().addResponse(request.getToken(), response); + processor.getScheduler().addResponse(request.getToken(), response); } catch (InterruptedException e) { - killer.interrupt(); return; } } } - - private synchronized Response subscribe(InternalSubscribeRequest req) { - SubscribeProcessorBeans.subscribeRequest(); - - EventHandler eventHandler = req.getEventHandler(); - String sparql = req.getSparql(); - String alias = req.getAlias(); - String defaultGraph = req.getDefaultGraphUri(); - String namedGraph = req.getNamedGraphUri(); - InternalSubscribeRequest wrappedRequest = new InternalSubscribeRequest(sparql, alias, defaultGraph, namedGraph, - this); - - // Get an SPU from the SPU manager (already available or a new one) - SPU spu = processor.getSPUManager().getSPU(wrappedRequest); - if (spu == null) - return new ErrorResponse(500, "internal_server_error", "Failed to create SPU"); - - // Generate a fake SPU id - String spuid = processor.getSPUManager().generateSpuid(); - - // Register handler - registerHandler(spu.getUUID(), spuid, eventHandler); - - return new SubscribeResponse(spuid, req.getAlias(), spu.getLastBindings()); - } - - private synchronized Response unsubscribe(String spuid) { - SubscribeProcessorBeans.unsubscribeRequest(); - - //synchronized (activeSpus) { - String masterSpuid = spuids.get(spuid); - logger.trace("@unsubscribe spuid: " + spuid); - - if (masterSpuid == null) { - logger.error("SPUID not found: " + spuid); - return new ErrorResponse(404, "spuid_not_found", "SPUID not found: " + spuid); - } - - // Unregister handler - unregisterHandler(masterSpuid, spuid); - //} - - return new UnsubscribeResponse(spuid); - } - -// public void killSpu(String spuid) { -// synchronized (activeSpus) { -// unregisterHandler(spuids.get(spuid), spuid); -// } -// } - - @Override - public synchronized void notifyEvent(Notification notify) { - // synchronized (handlers) { - logger.debug("@notifyEvent: " + notify); - - String spuid = notify.getSpuid(); - -// ArrayList toBeKilled = new ArrayList(); - - //synchronized (activeSpus) { - if (activeSpus.containsKey(spuid)) { - for (String client : activeSpus.get(spuid)) { - try { - // Dispatching events - Notification event = new Notification(client, notify.getARBindingsResults(), - sequenceNumbers.get(client)); - subscribers.get(client).notifyEvent(event); - sequenceNumbers.put(client, sequenceNumbers.get(client) + 1); - } catch (Exception e) { - logger.error(e.getMessage()); - -// // Handler is gone: unregister it -// toBeKilled.add(client); - } - } - -// for (String client : toBeKilled) -// unregisterHandler(spuid, client); - } - //} - } - - private synchronized void registerHandler(String masterSpuid, String spuid, EventHandler handler) { - logger.debug("SPU: "+masterSpuid + " register handler: " + spuid); - - //synchronized (activeSpus) { - // Register subscriber - subscribers.put(spuid, handler); - sequenceNumbers.put(spuid, 1); - spuids.put(spuid, masterSpuid); - - SubscribeProcessorBeans.addSubscriber(); - - // Link SPUID to active SPU - if (activeSpus.get(masterSpuid) == null) - activeSpus.put(masterSpuid, new ArrayList()); - activeSpus.get(masterSpuid).add(spuid); - //} - } - - private synchronized void unregisterHandler(String masterSpuid, String spuid) { - logger.debug("Unregister handler: " + spuid + " SPUID: "+masterSpuid); - - // SPUids - //synchronized (activeSpus) { - // Unregister subscriber - spuids.remove(spuid); - sequenceNumbers.remove(spuid); - subscribers.remove(spuid); - - SubscribeProcessorBeans.removeSubscriber(); - - // Subscriber not yet activated ==> return - if (!activeSpus.containsKey(masterSpuid)) return; - - activeSpus.get(masterSpuid).remove(spuid); - - if (activeSpus.get(masterSpuid).isEmpty()) { - activeSpus.remove(masterSpuid); - - // Deactivate SPU - logger.debug("Deactivate SPU: "+masterSpuid); - processor.getSPUManager().deactivate(masterSpuid); - } - //} - } - - @Override - public long getUpdateRequests() { - return SubscribeProcessorBeans.getUpdateRequests(); - } - - @Override - public long getSPUs_current() { - return SubscribeProcessorBeans.getSPUs_current(); - } - - @Override - public long getSPUs_max() { - return SubscribeProcessorBeans.getSPUs_max(); - } - - @Override - public float getSPUs_time() { - return SubscribeProcessorBeans.getSPUs_time(); - } - - @Override - public void reset() { - SubscribeProcessorBeans.reset(); - } - - @Override - public float getSPUs_time_min() { - return SubscribeProcessorBeans.getSPUs_time_min(); - } - - @Override - public float getSPUs_time_max() { - return SubscribeProcessorBeans.getSPUs_time_max(); - } - - @Override - public float getSPUs_time_average() { - return SubscribeProcessorBeans.getSPUs_time_averaae(); - } - - @Override - public long getSubscribeRequests() { - return SubscribeProcessorBeans.getSubscribeRequests(); - } - - @Override - public long getUnsubscribeRequests() { - return SubscribeProcessorBeans.getUnsubscribeRequests(); - } - - @Override - public long getSPUProcessingTimeout() { - return SubscribeProcessorBeans.getSPUProcessingTimeout(); - } - - @Override - public void setSPUProcessingTimeout(long t) { - SubscribeProcessorBeans.setActiveSPUs(t); - } - - @Override - public void scale_ms() { - SubscribeProcessorBeans.scale_ms(); - } - - @Override - public void scale_us() { - SubscribeProcessorBeans.scale_us(); - } - - @Override - public void scale_ns() { - SubscribeProcessorBeans.scale_ns(); - } - - @Override - public String getUnitScale() { - return SubscribeProcessorBeans.getUnitScale(); - } - - @Override - public long getSubscribers() { - return SubscribeProcessorBeans.getSubscribers(); - } - - @Override - public long getSubscribers_max() { - return SubscribeProcessorBeans.getSubscribersMax(); - } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java index 293d8e6d..36e40d56 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/UpdateProcessingThread.java @@ -17,7 +17,7 @@ public void run() { while (processor.isRunning()) { ScheduledRequest request; try { - request = processor.getSchedulerQueue().waitUpdateRequest(); + request = processor.getScheduler().waitUpdateRequest(); } catch (InterruptedException e) { return; } @@ -26,16 +26,16 @@ public void run() { InternalUpdateRequest update = (InternalUpdateRequest)request.getRequest(); // Notify update (not reliable) - if (!processor.isUpdateReilable()) processor.getSchedulerQueue().addResponse(request.getToken(),new UpdateResponse("Processing: "+update)); + if (!processor.isUpdateReilable()) processor.getScheduler().addResponse(request.getToken(),new UpdateResponse("Processing: "+update)); // Process update request Response ret = processor.getUpdateProcessor().process(update); // Notify update result - if (processor.isUpdateReilable()) processor.getSchedulerQueue().addResponse(request.getToken(),ret); + if (processor.isUpdateReilable()) processor.getScheduler().addResponse(request.getToken(),ret); // Subscription processing - if (ret.isUpdateResponse()) processor.getSPUManager().process((UpdateResponse) ret); + if (ret.isUpdateResponse()) processor.process((UpdateResponse) ret); } } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java index e68881ca..a92046e1 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/ISPU.java @@ -10,7 +10,7 @@ interface ISPU { BindingsResults getLastBindings(); - String getUUID(); + String getSPUID(); void process(UpdateResponse res); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java index 6142a09b..1d67b4ef 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPU.java @@ -18,6 +18,7 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; +import java.util.UUID; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; @@ -42,10 +43,10 @@ */ public abstract class SPU extends Thread implements ISPU { - private final Logger logger; + protected final Logger logger = LogManager.getLogger(); - // The URI of the subscription (i.e., sepa://spuid/UUID) - private String uuid = null; + // SPU identifier + protected String spuid; // Update queue private final LinkedBlockingQueue updateQueue = new LinkedBlockingQueue(); @@ -62,14 +63,12 @@ public abstract class SPU extends Thread implements ISPU { private Response notify; // List of processing SPU - protected SPUManager manager; + protected final SPUManager manager; public SPU(InternalSubscribeRequest subscribe, SPUManager manager) { this.manager = manager; this.subscribe = subscribe; - - uuid = manager.generateSpuid(); - logger = LogManager.getLogger("SPU" + uuid); + this.spuid = "sepa://spu/" + UUID.randomUUID(); } public InternalSubscribeRequest getSubscribe() { @@ -89,14 +88,17 @@ public void finish() { @Override public boolean equals(Object obj) { - if (!obj.getClass().equals(SPU.class)) - return false; - return ((SPU) obj).getUUID().equals(getUUID()); + return ((SPU) obj).subscribe.equals(subscribe); } @Override - public String getUUID() { - return uuid; + public String getSPUID() { + return spuid; + } + + @Override + public int hashCode() { + return subscribe.hashCode(); } @Override @@ -137,7 +139,7 @@ public void run() { // Notify SPU manager logger.debug("Notify SPU manager. Running: " + running); - manager.endProcessing(this); + manager.endOfProcessing(this); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index 01258d27..4ea85410 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -1,9 +1,16 @@ package it.unibo.arces.wot.sepa.engine.processing.subscriptions; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; +import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; import it.unibo.arces.wot.sepa.commons.response.UpdateResponse; -import it.unibo.arces.wot.sepa.engine.bean.SubscribeProcessorBeans; +import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; +import it.unibo.arces.wot.sepa.engine.bean.SPUManagerBeans; +import it.unibo.arces.wot.sepa.engine.core.EventHandler; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.processing.Processor; import it.unibo.arces.wot.sepa.engine.processing.QueryProcessor; import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; @@ -12,7 +19,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.UUID; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -21,108 +27,213 @@ * SpuManager is a monitor class. It takes care of the SPU collection and it * encapsulates filtering algorithms based on the internal structure. */ -public class SPUManager { +public class SPUManager implements SPUManagerMBean, EventHandler { private final Logger logger = LogManager.getLogger(); + // SPUs processing pool + private final HashSet processingPool = new HashSet(); + // SPUID ==> SPU private final HashMap spus = new HashMap(); - // Request ==> SPU - private final HashMap request2Spu = new HashMap(); + // SID ==> Subscriber + private final HashMap subscribers = new HashMap(); - // SPUID ==> Request - private final HashMap spuid2Request = new HashMap(); + // SPU ==> Subscribers + private final HashMap> handlers = new HashMap>(); - // SPUs processing set - private final HashSet processingSpus = new HashSet(); + // Request ==> SPU + private final HashMap requests = new HashMap(); private final Processor processor; public SPUManager(Processor processor) { this.processor = processor; - } - - public QueryProcessor getQueryProcessor() { - return processor.getQueryProcessor(); - } - public String generateSpuid() { - return "sepa://spuid/" + UUID.randomUUID().toString(); + SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); } public synchronized void process(UpdateResponse update) { logger.debug("*** PROCESSING SUBSCRIPTIONS BEGIN *** "); long start = Timings.getTime(); - processingSpus.clear(); + processingPool.clear(); Iterator spus = filter(update); while (spus.hasNext()) { SPU spu = spus.next(); - processingSpus.add(spu); + processingPool.add(spu); spu.process(update); } - logger.debug("Activated SPUs: " + processingSpus.size()); + logger.debug("@process SPU processing pool size: " + processingPool.size()); - while (!processingSpus.isEmpty()) { - logger.debug(String.format("Wait (%s) SPUs to complete processing...", processingSpus.size())); + while (!processingPool.isEmpty()) { + logger.debug(String.format("@process wait (%s) SPUs to complete processing...", processingPool.size())); try { - wait(SubscribeProcessorBeans.getSPUProcessingTimeout()); + wait(SPUManagerBeans.getSPUProcessingTimeout()); } catch (InterruptedException e) { return; } } // TIMEOUT - if (!processingSpus.isEmpty()) { - logger.error("Timeout on SPU processing. SPUs still running: " + processingSpus.size()); + if (!processingPool.isEmpty()) { + logger.error("@process timeout on SPU processing. SPUs still running: " + processingPool.size()); } long stop = Timings.getTime(); - SubscribeProcessorBeans.timings(start, stop); + SPUManagerBeans.timings(start, stop); logger.debug("*** PROCESSING SUBSCRIPTIONS END *** "); } - public synchronized void endProcessing(SPU s) { - logger.debug("EOP: " + s.getUUID()); - processingSpus.remove(s); + public synchronized void endOfProcessing(SPU s) { + logger.debug("@process EOP: " + s.getSPUID()); + processingPool.remove(s); notify(); } - private Iterator filter(UpdateResponse response) { - return spus.values().iterator(); + public synchronized Response subscribe(InternalSubscribeRequest req) { + SPUManagerBeans.subscribeRequest(); + + // Set the SPU Manager as event handler + String sparql = req.getSparql(); + String alias = req.getAlias(); + String defaultGraph = req.getDefaultGraphUri(); + String namedGraph = req.getNamedGraphUri(); + InternalSubscribeRequest wrappedRequest = new InternalSubscribeRequest(sparql, alias, defaultGraph, namedGraph, + this); + + // Create or link to an existing SPU + SPU spu; + if (requests.containsKey(req)) { + spu = requests.get(req); + } else { + spu = createSPU(wrappedRequest, this); + + // Initialize SPU + Response init = spu.init(); + if (init.isError()) { + logger.error("@subscribe SPU initialization failed: " + init); + return init; + } + + // Register request + requests.put(req, spu); + + // Create new entry for handler + synchronized (handlers) { + handlers.put(spu.getSPUID(), new HashSet()); + } + spus.put(spu.getSPUID(), spu); + + // Start the SPU thread + spu.setName(spu.getSPUID()); + spu.start(); + + synchronized (handlers) { + SPUManagerBeans.setActiveSPUs(handlers.size()); + } + logger.debug("@subscribe SPU activated: " + spu.getSPUID() + " total (" + handlers.size() + ")"); + } + + // New subscriber + Subscriber sub = new Subscriber(spu, req.getEventHandler()); + synchronized (handlers) { + handlers.get(spu.getSPUID()).add(sub); + } + subscribers.put(sub.getSID(), sub); + + SPUManagerBeans.addSubscriber(); + + Dependability.onSubscribe(sub.getGID(), sub.getSID()); + + return new SubscribeResponse(sub.getSID(), req.getAlias(), sub.getSPU().getLastBindings()); } - - public synchronized SPU getSPU(InternalSubscribeRequest req) { - if (request2Spu.containsKey(req)) - return request2Spu.get(req); + public synchronized Response unsubscribe(String sid, String gid) { + return internalUnsubscribe(sid, gid, true); + } - // Create SPU - SPU spu= createSPU(req,this); - if (spu == null) { - logger.error("SPU creation failed: "+req); - return spu; + public synchronized void killSubscription(String sid, String gid) { + internalUnsubscribe(sid, gid, false); + } + + private Response internalUnsubscribe(String sid, String gid, boolean dep) { + if (!subscribers.containsKey(sid)) { + logger.warn("@unsubscribe SID not found: " + sid); + return new ErrorResponse(500, "sid_not_found", "Unregistering a not existing subscriber: " + sid); } - // Initialize SPU - Response init = spu.init(); - if (init.isError()) { - logger.error("SPU initialization failed"); - return null; + // Remove subscriber + Subscriber sub = subscribers.get(sid); + String spuid = sub.getSPU().getSPUID(); + + logger.trace("@unsubscribe SID: " + sid + " from SPU: " + spuid + " with active subscriptions: " + + subscribers.size()); + synchronized (handlers) { + handlers.get(spuid).remove(sub); + } + subscribers.remove(sid); + + SPUManagerBeans.removeSubscriber(); + + // No more handlers: remove SPU + synchronized (handlers) { + if (handlers.get(spuid).isEmpty()) { + logger.debug("@unsubscribe no more subscribers. Kill SPU: " + sub.getSPU().getSPUID()); + + // If it is the last handler: kill SPU + spus.get(spuid).finish(); + spus.get(spuid).interrupt(); + + // Clear + spus.remove(spuid); + requests.remove(sub.getSPU().getSubscribe()); + handlers.remove(spuid); + + logger.debug("@unsubscribe active SPUs: " + spus.size()); + SPUManagerBeans.setActiveSPUs(spus.size()); + } } - // Activate SPU - activate(spu, req); + if (dep) + Dependability.onUnsubscribe(gid, sid); + + return new UnsubscribeResponse(sid); + } + + @Override + public void notifyEvent(Notification notify) throws SEPAProtocolException { + logger.debug("@notifyEvent " + notify); + + String spuid = notify.getSpuid(); + + if (spus.containsKey(spuid)) { + + synchronized (handlers) { + for (Subscriber client : handlers.get(spuid)) { + try { + // Dispatching events + Notification event = new Notification(client.getSID(), notify.getARBindingsResults(), + client.nextSequence()); + client.getHandler().notifyEvent(event); + } catch (Exception e) { + logger.error("@notifyEvent " + e.getMessage()); + } + } + } + } + } - return spu; + public QueryProcessor getQueryProcessor() { + return processor.getQueryProcessor(); } - + // TODO: choose different kinds of SPU based on subscribe request - private SPU createSPU(InternalSubscribeRequest req,SPUManager manager) { + private SPU createSPU(InternalSubscribeRequest req, SPUManager manager) { try { return new SPUNaive(req, this); } catch (SEPAProtocolException e) { @@ -130,38 +241,97 @@ private SPU createSPU(InternalSubscribeRequest req,SPUManager manager) { } } - public synchronized void activate(SPU spu, InternalSubscribeRequest request) { - // Start the SPU thread - logger.trace("Starting SPU: " + spu.getUUID()); - spu.setName("SPU_" + spu.getUUID()); - spu.start(); + private Iterator filter(UpdateResponse response) { + return spus.values().iterator(); + } - // Register the SPU as ACTIVE - logger.trace("Registering SPU: " + spu.getUUID()); - spus.put(spu.getUUID(), spu); - request2Spu.put(request, spu); - spuid2Request.put(spu.getUUID(), request); + @Override + public long getUpdateRequests() { + return SPUManagerBeans.getUpdateRequests(); + } - SubscribeProcessorBeans.setActiveSPUs(spus.values().size()); - logger.debug("ACTIVATE SPU: "+spu.getUUID() + " total (" + spus.values().size() + ")"); + @Override + public long getSPUs_current() { + return SPUManagerBeans.getSPUs_current(); } - public synchronized void deactivate(String spuid) { - if (!spus.containsKey(spuid)) { - logger.warn("Unregistering a not existing SPUID: " + spuid); - return; - } + @Override + public long getSPUs_max() { + return SPUManagerBeans.getSPUs_max(); + } - // Stop SPU - spus.get(spuid).finish(); - spus.get(spuid).interrupt(); - - // Remove from active SPUs - spus.remove(spuid); - request2Spu.remove(spuid2Request.get(spuid)); - spuid2Request.remove(spuid); + @Override + public float getSPUs_time() { + return SPUManagerBeans.getSPUs_time(); + } + + @Override + public void reset() { + SPUManagerBeans.reset(); + } + + @Override + public float getSPUs_time_min() { + return SPUManagerBeans.getSPUs_time_min(); + } + + @Override + public float getSPUs_time_max() { + return SPUManagerBeans.getSPUs_time_max(); + } + + @Override + public float getSPUs_time_average() { + return SPUManagerBeans.getSPUs_time_averaae(); + } + + @Override + public long getSubscribeRequests() { + return SPUManagerBeans.getSubscribeRequests(); + } + + @Override + public long getUnsubscribeRequests() { + return SPUManagerBeans.getUnsubscribeRequests(); + } + + @Override + public long getSPUProcessingTimeout() { + return SPUManagerBeans.getSPUProcessingTimeout(); + } + + @Override + public void setSPUProcessingTimeout(long t) { + SPUManagerBeans.setActiveSPUs(t); + } + + @Override + public void scale_ms() { + SPUManagerBeans.scale_ms(); + } + + @Override + public void scale_us() { + SPUManagerBeans.scale_us(); + } + + @Override + public void scale_ns() { + SPUManagerBeans.scale_ns(); + } + + @Override + public String getUnitScale() { + return SPUManagerBeans.getUnitScale(); + } + + @Override + public long getSubscribers() { + return SPUManagerBeans.getSubscribers(); + } - SubscribeProcessorBeans.setActiveSPUs(spus.values().size()); - logger.debug("Active SPUs: " + spus.values().size()); + @Override + public long getSubscribers_max() { + return SPUManagerBeans.getSubscribersMax(); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManagerMBean.java similarity index 85% rename from engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java rename to engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManagerMBean.java index f99b465b..b7d843dc 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SubscribeProcessingThreadMBean.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManagerMBean.java @@ -1,6 +1,6 @@ -package it.unibo.arces.wot.sepa.engine.processing; +package it.unibo.arces.wot.sepa.engine.processing.subscriptions; -public interface SubscribeProcessingThreadMBean { +public interface SPUManagerMBean { public long getUpdateRequests(); public long getSubscribeRequests(); public long getUnsubscribeRequests(); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java index c0c36bdd..706519be 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUNaive.java @@ -32,6 +32,8 @@ import it.unibo.arces.wot.sepa.commons.sparql.BindingsResults; import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; +import java.util.UUID; + import org.apache.logging.log4j.LogManager; class SPUNaive extends SPU { @@ -40,8 +42,10 @@ class SPUNaive extends SPU { public SPUNaive(InternalSubscribeRequest subscribe, SPUManager manager) throws SEPAProtocolException { super(subscribe, manager); - logger = LogManager.getLogger("SPUNaive" + getUUID()); - logger.debug("SPU: " + this.getUUID() + " request: " + subscribe); + this.spuid = "sepa://spu/naive/" + UUID.randomUUID(); + + logger = LogManager.getLogger("SPUNaive" + getSPUID()); + logger.debug("SPU: " + this.getSPUID() + " request: " + subscribe); } @Override @@ -60,7 +64,7 @@ public Response init() { logger.debug("First results: " + lastBindings.toString()); - return new SubscribeResponse(getUUID(), subscribe.getAlias(), lastBindings); + return new SubscribeResponse(getSPUID(), subscribe.getAlias(), lastBindings); } @Override @@ -117,7 +121,7 @@ public Response processInternal(UpdateResponse update) { // Send notification (or end processing indication) if (!added.isEmpty() || !removed.isEmpty()) - ret = new Notification(getUUID(), new ARBindingsResults(added, removed)); + ret = new Notification(getSPUID(), new ARBindingsResults(added, removed)); } catch (Exception e) { ret = new ErrorResponse(500, "Exception: ",e.getMessage()); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java new file mode 100644 index 00000000..c0d8631c --- /dev/null +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/Subscriber.java @@ -0,0 +1,45 @@ +package it.unibo.arces.wot.sepa.engine.processing.subscriptions; + +import java.util.UUID; + +import it.arces.wot.sepa.engine.gates.Gate; +import it.unibo.arces.wot.sepa.engine.core.EventHandler; + +public class Subscriber { + private final SPU spu; + private final EventHandler handler; + + // Subscriber Identifier + private final String sid; + + private int sequence = 0; + + public Subscriber(SPU spu,EventHandler handler) { + this.spu = spu; + this.handler = handler; + + sid = "sepa://subscription/" + UUID.randomUUID().toString(); + } + + public int nextSequence() { + sequence++; + return sequence; + } + + public EventHandler getHandler() { + return handler; + } + + public String getSID() { + return sid; + } + + public String getGID() { + return ((Gate)handler).getGID(); + } + + public SPU getSPU() { + return spu; + } + +} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java index 1ff9de7a..25f8b19a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/HttpsGate.java @@ -15,7 +15,7 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.engine.bean.EngineBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.protocol.http.handler.EchoHandler; import it.unibo.arces.wot.sepa.engine.protocol.http.handler.RegisterHandler; import it.unibo.arces.wot.sepa.engine.protocol.http.handler.SecureQueryHandler; @@ -34,21 +34,18 @@ public class HttpsGate { protected IOReactorConfig config = IOReactorConfig.custom().setTcpNoDelay(true).setSoReuseAddress(true).build(); - protected AuthorizationManager oauth; - - public HttpsGate(EngineProperties properties, Scheduler scheduler, AuthorizationManager oauth) throws SEPASecurityException, SEPAProtocolException { - this.oauth = oauth; + public HttpsGate(EngineProperties properties, Scheduler scheduler) throws SEPASecurityException, SEPAProtocolException { try { server = ServerBootstrap.bootstrap().setListenerPort(properties.getHttpsPort()).setServerInfo(serverInfo) - .setIOReactorConfig(config).setSslContext(oauth.getSSLContext()) + .setIOReactorConfig(config).setSslContext(Dependability.getSSLContext()) .setExceptionLogger(ExceptionLogger.STD_ERR) - .registerHandler(properties.getRegisterPath(), new RegisterHandler(oauth)) + .registerHandler(properties.getRegisterPath(), new RegisterHandler()) .registerHandler(properties.getSecurePath() + properties.getQueryPath(), - new SecureQueryHandler(scheduler, oauth)) + new SecureQueryHandler(scheduler)) .registerHandler(properties.getSecurePath() + properties.getUpdatePath(), - new SecureUpdateHandler(scheduler, oauth)) - .registerHandler(properties.getTokenRequestPath(), new JWTRequestHandler(oauth)) + new SecureUpdateHandler(scheduler)) + .registerHandler(properties.getTokenRequestPath(), new JWTRequestHandler()) .registerHandler("/echo", new EchoHandler()).create(); } catch (IllegalArgumentException e) { throw new SEPASecurityException(e); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java index 11e807a6..ffdb8303 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/JWTRequestHandler.java @@ -16,18 +16,14 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; public class JWTRequestHandler implements HttpAsyncRequestHandler { protected static final Logger logger = LogManager.getLogger(); - private AuthorizationManager am; + public JWTRequestHandler() throws IllegalArgumentException { - public JWTRequestHandler(AuthorizationManager am) throws IllegalArgumentException { - if (am == null) - throw new IllegalArgumentException(); - this.am = am; } @Override @@ -100,7 +96,7 @@ public void handle(HttpRequest request, HttpAsyncExchange httpExchange, HttpCont // ************* // Get token // ************* - Response token = am.getToken(basic.split(" ")[1]); + Response token = Dependability.getToken(basic.split(" ")[1]); if (token.getClass().equals(ErrorResponse.class)) { ErrorResponse error = (ErrorResponse) token; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java index 7c2b189a..38be9880 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java @@ -26,16 +26,14 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; public class RegisterHandler implements HttpAsyncRequestHandler { private static final Logger logger = LogManager.getLogger(); - private AuthorizationManager am; + public RegisterHandler() { - public RegisterHandler(AuthorizationManager am) { - this.am = am; } @Override @@ -131,7 +129,7 @@ public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext con // ***************************************** // Register client and retrieve credentials // ***************************************** - Response cred = am.register(name); + Response cred = Dependability.register(name); if (cred.getClass().equals(ErrorResponse.class)) { ErrorResponse error = (ErrorResponse) cred; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java index 686bf8e8..5021e924 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11Handler.java @@ -26,7 +26,7 @@ import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.bean.HTTPHandlerBeans; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; -import it.unibo.arces.wot.sepa.engine.dependability.CORSManager; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.engine.scheduling.InternalQueryRequest; import it.unibo.arces.wot.sepa.engine.scheduling.InternalUQRequest; @@ -61,13 +61,13 @@ protected boolean validate(HttpRequest request) { protected abstract Response authorize(HttpRequest request); protected boolean corsHandling(HttpAsyncExchange exchange) { - if (!CORSManager.processCORSRequest(exchange)) { + if (!Dependability.processCORSRequest(exchange)) { logger.error("CORS origin not allowed"); HttpUtilities.sendFailureResponse(exchange, new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "cors_error","CORS origin not allowed")); return false; } - if (CORSManager.isPreFlightRequest(exchange)) { + if (Dependability.isPreFlightRequest(exchange)) { logger.error("Preflight request"); HttpUtilities.sendResponse(exchange, HttpStatus.SC_NO_CONTENT, ""); return false; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java index a90b0771..d7f5b9d7 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SPARQL11ResponseHandler.java @@ -13,7 +13,7 @@ import it.unibo.arces.wot.sepa.engine.protocol.http.HttpUtilities; import it.unibo.arces.wot.sepa.timing.Timings; -public class SPARQL11ResponseHandler extends ResponseHandler { +public class SPARQL11ResponseHandler implements ResponseHandler { protected final Logger logger = LogManager.getLogger(); private HttpAsyncExchange handler; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java index 9354c784..1eade2d8 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureQueryHandler.java @@ -6,17 +6,13 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureQueryHandler extends QueryHandler implements SecureQueryHandlerMBean { - private AuthorizationManager am; - - public SecureQueryHandler(Scheduler scheduler, AuthorizationManager am) throws IllegalArgumentException { + public SecureQueryHandler(Scheduler scheduler) throws IllegalArgumentException { super(scheduler); - - this.am = am; } /** @@ -61,7 +57,7 @@ protected Response authorize(HttpRequest request) { // ****************** String jwt = bearer[0].getValue().split(" ")[1]; - return am.validateToken(jwt); + return Dependability.validateToken(jwt); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java index f61bdc1b..b44abc76 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/SecureUpdateHandler.java @@ -6,17 +6,13 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; public class SecureUpdateHandler extends UpdateHandler implements SecureUpdateHandlerMBean { - private AuthorizationManager am; - - public SecureUpdateHandler(Scheduler scheduler, AuthorizationManager am) throws IllegalArgumentException { + public SecureUpdateHandler(Scheduler scheduler) throws IllegalArgumentException { super(scheduler); - - this.am = am; } /** @@ -61,7 +57,7 @@ protected Response authorize(HttpRequest request) { // ****************** String jwt = bearer[0].getValue().split(" ")[1]; - return am.validateToken(jwt); + return Dependability.validateToken(jwt); } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java index 0155e8b9..18e81f0d 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServer.java @@ -1,171 +1,48 @@ package it.unibo.arces.wot.sepa.engine.protocol.websocket; -import org.apache.http.HttpStatus; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import org.java_websocket.WebSocket; import org.java_websocket.server.DefaultSSLWebSocketServerFactory; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; - import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; - -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; -import it.unibo.arces.wot.sepa.engine.dependability.AuthorizationManager; - -import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; +import it.unibo.arces.wot.sepa.engine.dependability.Dependability; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; -public class SecureWebsocketServer extends WebsocketServer implements SecureWebsocketServerMBean { - private AuthorizationManager oauth; - private final static Logger logger = LogManager.getLogger(); +public class SecureWebsocketServer extends WebsocketServer { + //private final static Logger logger = LogManager.getLogger(); @Override protected String getWelcomeMessage() { return "SPARQL 1.1 Subscribe | wss://%s:%d%s"; } - public SecureWebsocketServer(int port, String path, Scheduler scheduler, AuthorizationManager oauth) + public SecureWebsocketServer(int port, String path, Scheduler scheduler) throws SEPAProtocolException, SEPASecurityException { super(port, path, scheduler); - if (oauth == null) - throw new IllegalArgumentException("Authorization manager is null"); - - this.oauth = oauth; - - setWebSocketFactory(new DefaultSSLWebSocketServerFactory(oauth.getSSLContext())); - } - - @Override - protected InternalRequest parseRequest(String request, WebSocket conn) - throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException { - JsonObject req; - - try{ - req = new JsonParser().parse(request).getAsJsonObject(); - } - catch(Exception e) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception","Exception: " + request); - conn.send(error.toString()); - logger.error(error); - return null; - } - - // CHECK AUTHORIZATION - Response ret = validateRequest(req); - - if (ret.isError()) { - // Not authorized - WebsocketBeans.onNotAuthorizedRequest(); - - logger.warn("NOT AUTHORIZED"); - conn.send(ret.toString()); - return null; - } - - return super.parseRequest(request, conn); - } -/** -
    -	Specific to SPARQL 1.1 SE Subscribe request:
    -	1. Check if the request contains an "authorization" member. 
    -	2. Check if the request contains an "authorization" member that start with "Bearer" 
    -	3. Check if the value of the "authorization" member is a JWT object ==> VALIDATE TOKEN
    -
    -	Token validation:
    -	4. Check if the JWT object is signed 
    -	5. Check if the signature of the JWT object is valid. This is to be checked with AS public signature verification key 
    -	6. Check the contents of the JWT object 
    -	7. Check if the value of "iss" is https://wot.arces.unibo.it:8443/oauth/token 
    -	8. Check if the value of "aud" contains https://wot.arces.unibo.it:8443/sparql 
    -	9. Accept the request as well as "sub" as the originator of the request and process it as usual
    -	 
    -	Respond with 401 if not
    -	 
    -	
    - */ - private Response validateRequest(JsonObject request) { - String bearer = null; - JsonObject subUnsub = null; - - if (request.has("subscribe")) subUnsub = request.get("subscribe").getAsJsonObject(); - else if (request.has("unsubscribe")) subUnsub = request.get("unsubscribe").getAsJsonObject(); - - if (subUnsub == null) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "invalid_request","Neither subscribe or unsuscribe found"); - logger.error(error); - return error; - } - - if (!subUnsub.has("authorization")) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization member is missing"); - logger.error(error); - return error; - } - - try{ - bearer = subUnsub.get("authorization").getAsString(); - } - catch(Exception e) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization member is not a string"); - logger.error(error); - return error; - } - - if (!bearer.startsWith("Bearer ")) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "unauthorized_client","Authorization value MUST be of type Bearer"); - logger.error(error); - return error; - } - - String jwt = bearer.substring(7); - - if (jwt == null) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Token is null"); - logger.error(error); - return error; - } - if (jwt.equals("")) { - ErrorResponse error = new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"unauthorized_client", "Token is empty"); - logger.error(error); - return error; - } - - // Token validation - return oauth.validateToken(jwt); - } - - @Override - public void onClose(WebSocket conn, int code, String reason, boolean remote) { - super.onClose(conn, code, reason, remote); - } - - @Override - public void onError(WebSocket conn, Exception ex) { - super.onError(conn, ex); - } - - @Override - public void onStart() { - System.out.println(welcomeMessage); - - synchronized (this) { - notify(); - } - } - - @Override - public long getNotAuthorized() { - return WebsocketBeans.getNotAuthorized(); - } - + setWebSocketFactory(new DefaultSSLWebSocketServerFactory(Dependability.getSSLContext())); + } + +// @Override +// public void onOpen(WebSocket conn, ClientHandshake handshake) { +// logger.trace("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); +// +// if (!conn.getResourceDescriptor().equals(path)) { +// logger.warn("@onOpen Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); +// ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND, "wrong_path", +// "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); +// conn.send(response.toString()); +// return; +// } +// +// // Add new gate +// synchronized (gates) { +// WebsocketGate handler = new SecureWebsocketGate(conn, scheduler); +// gates.put(conn, handler); +// +// fragmentedMessages.put(conn, null); +// +// logger.debug("@onOpen socket: " + conn.hashCode() + " UUID: " + handler.getGID() + " Total sockets: " +// + gates.size()); +// } +// } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServerMBean.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServerMBean.java deleted file mode 100644 index 359104cc..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/SecureWebsocketServerMBean.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.protocol.websocket; - -public interface SecureWebsocketServerMBean extends WebsocketServerMBean { - public long getNotAuthorized(); -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java deleted file mode 100644 index 9a71ef82..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketEventHandler.java +++ /dev/null @@ -1,48 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.protocol.websocket; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.java_websocket.WebSocket; -import org.java_websocket.exceptions.WebsocketNotConnectedException; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.response.Response; -import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; -import it.unibo.arces.wot.sepa.engine.core.EventHandler; -import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; - -public class WebsocketEventHandler extends ResponseHandler implements EventHandler { - private static final Logger logger = LogManager.getLogger(); - - private final WebSocket socket; - - public WebsocketEventHandler(WebSocket s){ - this.socket = s; - } - - private void send(Response ret) throws SEPAProtocolException { - try{ - socket.send(ret.toString()); - } - catch(WebsocketNotConnectedException e){ - logger.error("Socket: "+socket.hashCode()+" failed to send response: "+ret+" Exception:"+e.getMessage()); - throw new SEPAProtocolException(e.getMessage()); - } - } - - @Override - public void sendResponse(Response response) throws SEPAProtocolException { - logger.trace(response); - - - send(response); - } - - @Override - public void notifyEvent(Notification notify) throws SEPAProtocolException { - WebsocketBeans.notification(); - - send(notify); - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketRequest.java deleted file mode 100644 index 29ae1a6f..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.protocol.websocket; - -import org.java_websocket.WebSocket; - -public class WebsocketRequest { - private WebSocket socket; - private String message; - - public WebsocketRequest(WebSocket socket,String message) { - this.socket = socket; - this.message = message; - } - - public WebSocket getSocket() { - return socket; - } - - public String getMessage() { - return message; - } -} diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index a4529d66..9720cbbd 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -6,7 +6,6 @@ import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.HashMap; -import java.util.UUID; import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; @@ -17,50 +16,28 @@ import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonSyntaxException; - +import it.arces.wot.sepa.engine.gates.WebsocketGate; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; -import it.unibo.arces.wot.sepa.engine.scheduling.InternalRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.InternalSubscribeRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.InternalUnsubscribeRequest; -import it.unibo.arces.wot.sepa.engine.scheduling.ScheduledRequest; import it.unibo.arces.wot.sepa.engine.scheduling.Scheduler; -import it.unibo.arces.wot.sepa.engine.timing.Timings; public class WebsocketServer extends WebSocketServer implements WebsocketServerMBean { - private static final Logger logger = LogManager.getLogger(); - - protected Scheduler scheduler; - - protected String getWelcomeMessage() { - return "SPARQL 1.1 Subscribe | ws://%s:%d%s"; - } - - protected String welcomeMessage; - - private String path; - + protected static final Logger logger = LogManager.getLogger(); + + // Active gates + protected final HashMap gates = new HashMap(); + // Fragmentation support - private final HashMap fragmentedMessages = new HashMap(); - - // Active sockets - protected final HashMap activeSockets = new HashMap(); + protected final HashMap fragmentedMessages = new HashMap(); - // Sockets UUIDs - protected final HashMap uuids = new HashMap(); + protected final Scheduler scheduler; + protected final String welcomeMessage; + protected final String path; - // Dependability manager - //private final DependabilityManager dependabilityMng; - - public WebsocketServer(int port, String path, Scheduler scheduler) - throws SEPAProtocolException { + public WebsocketServer(int port, String path, Scheduler scheduler) throws SEPAProtocolException { super(new InetSocketAddress(port)); if (path == null || scheduler == null) @@ -83,161 +60,67 @@ public WebsocketServer(int port, String path, Scheduler scheduler) welcomeMessage = String.format(getWelcomeMessage(), address, port, path); } + protected String getWelcomeMessage() { + return "SPARQL 1.1 Subscribe | ws://%s:%d%s"; + } + @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { - logger.debug("@onOpen: " + conn + " Resource descriptor: " + conn.getResourceDescriptor()); - - if (!conn.getResourceDescriptor().equals(path)) { - logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND, "wrong_path", - "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - conn.send(response.toString()); - return; - } + // Add new gate + synchronized (gates) { + WebsocketGate gate = new WebsocketGate(conn, scheduler); + + gates.put(conn, gate); - fragmentedMessages.put(conn, null); + fragmentedMessages.put(conn, null); - // Add active socket - if (!activeSockets.containsKey(conn)) { - WebsocketEventHandler handler = new WebsocketEventHandler(conn); - - uuids.put(conn, handler.getUUID()); - activeSockets.put(conn,handler); - - logger.debug("Handler UUID: "+handler.getUUID()+" Total handlers: "+activeSockets.size()); + logger.debug("@onOpen websocket: " + conn + " GID: " + gate.getGID() + " Total sockets: " + + gates.size()); } } @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { - logger.debug("@onClose: " + conn + " Reason: " + reason + " Code: " + code + " Remote: " + remote); - - if (!conn.getResourceDescriptor().equals(path)) - return; + synchronized (gates) { + logger.trace("@onClose socket: " + conn + " reason: " + reason + " remote: "+remote); - fragmentedMessages.remove(conn); + fragmentedMessages.remove(conn); - // KILL ALL SPUs - scheduler.onBrokenSubscription(uuids.get(conn)); + // Close gate + if (gates.get(conn) != null) gates.get(conn).close(); - // Remove active socket - activeSockets.remove(conn); + // Remove from active gates + gates.remove(conn); + } } @Override public void onMessage(WebSocket conn, String message) { WebsocketBeans.onMessage(); - logger.trace("Message from: " + conn.getRemoteSocketAddress() + " [" + message + "]"); - + // Check path if (!conn.getResourceDescriptor().equals(path)) { - logger.warn("Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); + logger.warn("@onMessage bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); + ErrorResponse response = new ErrorResponse(HttpStatus.SC_NOT_FOUND, "wrong_path", "Bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); - conn.send(response.toString()); - return; - } - - // Parse the request - InternalRequest req = parseRequest(message, conn); - if (req == null) { - logger.error("Failed to parse message: " + req); - activeSockets.remove(conn); - return; - } - - Timings.log(req); - - // Schedule the request - ScheduledRequest request = scheduler.schedule(req, activeSockets.get(conn)); - if (request == null) { - logger.error("Out of tokens"); - ErrorResponse response = new ErrorResponse(429, "too_many_requests", "Too many pending requests"); - conn.send(response.toString()); - } - } - - /** - * SPARQL 1.1 Subscribe language - * - *
    -	{"subscribe":{
    -		"sparql":"SPARQL Query 1.1", 
    -		"authorization": "Bearer JWT", (optional)
    -		"alias":"an alias for the subscription", (optional)
    -		"default-graph-uri": "graphURI", (optional)
    -		"named-graph-uri": "graphURI" (optional)
    -	}}
    -	
    -	{"unsubscribe":{
    -		"spuid":"SPUID", 
    -		"authorization": "Bearer JWT" (optional)
    -	}}
    -	 * 
    - */ - protected InternalRequest parseRequest(String request, WebSocket conn) - throws JsonParseException, JsonSyntaxException, IllegalStateException, ClassCastException { - JsonObject req; - ErrorResponse error; - - try { - req = new JsonParser().parse(request).getAsJsonObject(); - } catch (JsonParseException e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "JsonParseException", - "JsonParseException: " + request); - conn.send(error.toString()); - logger.error(error); - return null; - } - - if (req.has("subscribe")) { - String sparql = null; - String alias = null; - String defaultGraphUri = null; - String namedGraphUri = null; - - try { - sparql = req.get("subscribe").getAsJsonObject().get("sparql").getAsString(); - } catch (Exception e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception", - "sparql member not found: " + request); - conn.send(error.toString()); - logger.error(error); - return null; - } - - try { - alias = req.get("subscribe").getAsJsonObject().get("alias").getAsString(); - } catch (Exception e) { + + try{ + conn.send(response.toString()); } - - try { - defaultGraphUri = req.get("subscribe").getAsJsonObject().get("default-graph-uri").getAsString(); - } catch (Exception e) { - } - - try { - namedGraphUri = req.get("subscribe").getAsJsonObject().get("named-graph-uri").getAsString(); - } catch (Exception e) { + catch(Exception e) { + logger.warn(e.getMessage()); } + return; + } - return new InternalSubscribeRequest(sparql, alias, defaultGraphUri, namedGraphUri, activeSockets.get(conn)); - } else if (req.has("unsubscribe")) { - String spuid; + synchronized (gates) { try { - spuid = req.get("unsubscribe").getAsJsonObject().get("spuid").getAsString(); - } catch (Exception e) { - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "Exception", "spuid member not found: " + request); - conn.send(error.toString()); - return null; + if (gates.get(conn) != null) gates.get(conn).onMessage(message); + } catch (SEPAProtocolException e) { + logger.error(e); } - - return new InternalUnsubscribeRequest(spuid); } - - error = new ErrorResponse(HttpStatus.SC_BAD_REQUEST, "unsupported", "Bad request: " + request); - conn.send(error.toString()); - return null; } /** @@ -274,15 +157,17 @@ public void onFragment(WebSocket conn, Framedata fragment) { public void onError(WebSocket conn, Exception ex) { logger.error("@onError: " + conn + " Exception: " + ex); + WebsocketBeans.onError(); + if (ex.getClass().equals(BindException.class)) { logger.fatal("Failed to start. Exit"); System.exit(-1); } - if (!conn.getResourceDescriptor().equals(path)) + if (!conn.getResourceDescriptor().equals(path)) { + logger.warn("@onError bad resource descriptor: " + conn.getResourceDescriptor() + " Use: " + path); return; - - WebsocketBeans.onError(); + } } @Override diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java index 2cf86e5e..1e354d5f 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalQueryRequest.java @@ -10,10 +10,4 @@ public InternalQueryRequest(String sparql, String defaultGraphUri, String namedG public String toString() { return "*QUERY* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof InternalQueryRequest)) return false; - return sparql.equals(((InternalQueryRequest)obj).sparql); - } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java index afbbf62f..ba085001 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalRequest.java @@ -1,7 +1,6 @@ package it.unibo.arces.wot.sepa.engine.scheduling; public abstract class InternalRequest { - public boolean isQueryRequest() { return this.getClass().equals(InternalQueryRequest.class); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java index aa256265..4bdc14a2 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalSubscribeRequest.java @@ -5,13 +5,13 @@ public class InternalSubscribeRequest extends InternalQueryRequest { private String alias = null; - private EventHandler handler; + private EventHandler gate; - public InternalSubscribeRequest(String sparql, String alias,String defaultGraphUri, String namedGraphUri,EventHandler handler) { + public InternalSubscribeRequest(String sparql, String alias,String defaultGraphUri, String namedGraphUri,EventHandler gate) { super(sparql, defaultGraphUri, namedGraphUri); this.alias = alias; - this.handler = handler; + this.gate = gate; } @Override @@ -19,21 +19,11 @@ public String toString() { return "*SUBSCRIBE* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; } - public String getSpuid() { - return sparql; - } - public String getAlias() { return alias; } - @Override - public boolean equals(Object obj) { - if (!(obj instanceof InternalSubscribeRequest)) return false; - return sparql.equals(((InternalSubscribeRequest)obj).sparql); - } - public EventHandler getEventHandler() { - return handler; + return gate; } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java index 1d060daa..62aa3267 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUQRequest.java @@ -6,6 +6,8 @@ public abstract class InternalUQRequest extends InternalRequest { protected String namedGraphUri; public InternalUQRequest(String sparql,String defaultGraphUri,String namedGraphUri) { + if (sparql == null) throw new IllegalArgumentException("SPARQL is null"); + this.sparql = sparql; this.defaultGraphUri = defaultGraphUri; this.namedGraphUri = namedGraphUri; @@ -27,4 +29,9 @@ public String getNamedGraphUri() { public int hashCode() { return sparql.hashCode(); } + + @Override + public boolean equals(Object obj) { + return sparql.equals(((InternalUQRequest)obj).sparql); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java index 9f5bd32e..c48f68df 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUnsubscribeRequest.java @@ -1,24 +1,30 @@ package it.unibo.arces.wot.sepa.engine.scheduling; public class InternalUnsubscribeRequest extends InternalRequest { - protected String spuid; + protected String sid; + protected String gid; - public InternalUnsubscribeRequest(String spuid) { - this.spuid = spuid; + public InternalUnsubscribeRequest(String gid,String sid) { + this.sid = sid; + this.gid = gid; } @Override public String toString() { - return "*UNSUBSCRIBE* "+spuid; + return "*UNSUBSCRIBE* "+sid; } - public String getSpuid() { - return spuid; + public String getSID() { + return sid; + } + + public String getGID() { + return gid; } @Override public boolean equals(Object obj) { if (!(obj instanceof InternalUnsubscribeRequest)) return false; - return spuid.equals(((InternalUnsubscribeRequest)obj).spuid); + return sid.equals(((InternalUnsubscribeRequest)obj).sid); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java index bf169200..2e599110 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java @@ -10,10 +10,4 @@ public InternalUpdateRequest(String sparql, String defaultGraphUri, String named public String toString() { return "*UPDATE* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof InternalUpdateRequest)) return false; - return sparql.equals(((InternalUpdateRequest)obj).sparql); - } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 91cb153b..659c7af1 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -19,22 +19,17 @@ package it.unibo.arces.wot.sepa.engine.scheduling; import java.util.HashMap; -import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.SubscribeResponse; -import it.unibo.arces.wot.sepa.commons.response.UnsubscribeResponse; +import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.engine.bean.SEPABeans; import it.unibo.arces.wot.sepa.engine.bean.SchedulerBeans; -import it.unibo.arces.wot.sepa.engine.bean.WebsocketBeans; import it.unibo.arces.wot.sepa.engine.core.EngineProperties; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -import it.unibo.arces.wot.sepa.engine.dependability.DependabilityManager; import it.unibo.arces.wot.sepa.engine.timing.Timings; /** @@ -47,14 +42,11 @@ public class Scheduler extends Thread implements SchedulerMBean { private final AtomicBoolean running = new AtomicBoolean(true); // Responders - private HashMap responders = new HashMap(); - private HashMap handlers = new HashMap(); + private final HashMap responders = new HashMap(); // Synchronized queues private final SchedulerQueue queue; - private final DependabilityManager dependability; - public Scheduler(EngineProperties properties) { if (properties == null) { logger.error("Properties are null"); @@ -63,9 +55,6 @@ public Scheduler(EngineProperties properties) { queue = new SchedulerQueue(properties.getSchedulingQueueSize()); - // Dependability manager - dependability = new DependabilityManager(queue); - // JMX SEPABeans.registerMBean("SEPA:type=" + this.getClass().getSimpleName(), this); SchedulerBeans.setQueueSize(properties.getSchedulingQueueSize()); @@ -73,38 +62,39 @@ public Scheduler(EngineProperties properties) { setName("SEPA-Scheduler"); } + + public ScheduledRequest schedule(InternalRequest request, ResponseHandler handler) { + if (request == null || handler == null) { + logger.error("Request handler or request are null"); + return null; + } - public synchronized ScheduledRequest schedule(InternalRequest request, ResponseHandler handler) { - synchronized (responders) { - if (request == null || handler == null) { - logger.error("Request handler or request are null"); - return null; - } + // Add request to the scheduler queue (null means no more tokens) + ScheduledRequest scheduled = queue.addRequest(request, handler); - // Add request to the scheduler queue (null means no more tokens) - ScheduledRequest scheduled = queue.addRequest(request, handler); + // No more tokens + if (scheduled == null) { + SchedulerBeans.newRequest(request, false); + logger.error("Request refused: too many pending requests: " + request); + return null; + } - // No more tokens - if (scheduled == null) { - SchedulerBeans.newRequest(request, false); - logger.error("Request refused: too many pending requests: " + request); - return null; - } + logger.info(">> " + scheduled); - logger.info(">> " + scheduled); + Timings.log(request); - Timings.log(request); + SchedulerBeans.newRequest(request, true); - SchedulerBeans.newRequest(request, true); + // Register response handlers + responders.put(scheduled.getToken(), handler); - // Register response handlers - responders.put(scheduled.getToken(), handler); - handlers.put(scheduled.getToken(), handler.getUUID()); - - return scheduled; - } + return scheduled; } + public void finish() { + running.set(false); + } + @Override public void run() { while (running.get()) { @@ -116,51 +106,29 @@ public void run() { // The token int token = response.getToken(); - synchronized (responders) { - // Send response back - ResponseHandler handler = responders.get(token); - if (handler == null) { - logger.warn("Response handler is null (token #" + token + ")"); - } else { - try { - handler.sendResponse(response.getResponse()); - } catch (SEPAProtocolException e) { - logger.error("Failed to send response: " + e.getMessage()); - } - } - - // Dependability - if (response.getResponse().isSubscribeResponse()) { - WebsocketBeans.subscribeResponse(); - dependability.onSubscribe(handlers.get(token), - ((SubscribeResponse) response.getResponse()).getSpuid()); - } else if (response.getResponse().isUnsubscribeResponse()) { - WebsocketBeans.unsubscribeResponse(); - dependability.onUnsubscribe(handlers.get(token), - ((UnsubscribeResponse) response.getResponse()).getSpuid()); - } else if (response.getResponse().isError()) { - WebsocketBeans.errorResponse(); - logger.error(response); - dependability.onError(handlers.get(token), (ErrorResponse) response.getResponse()); + // Send response back + ResponseHandler handler = responders.get(token); + if (handler == null) { + logger.error("Response handler is null (token #" + token + ")"); + + } else { + try { + handler.sendResponse(response.getResponse()); + } catch (SEPAProtocolException e) { + logger.error("Failed to send response: " + e.getMessage()); } - - // Remove handlers - responders.remove(token); - handlers.remove(token); } + + // Remove handlers + responders.remove(token); + } catch (InterruptedException e) { running.set(false); } } } - public void onBrokenSubscription(UUID uuid) { - dependability.onBrokenSubscription(uuid); - } - - public void finish() { - running.set(false); - } + public String getStatistics() { return SchedulerBeans.getStatistics(); @@ -197,10 +165,6 @@ public int getQueueSize() { return SchedulerBeans.getQueueSize(); } - public SchedulerQueue getSchedulerQueue() { - return queue; - } - @Override public int getTimeout() { return SchedulerBeans.getTimeout(); @@ -210,4 +174,20 @@ public int getTimeout() { public void setTimeout(int timeout) { SchedulerBeans.setTimeout(timeout); } + + public ScheduledRequest waitQueryRequest() throws InterruptedException { + return queue.waitQueryRequest(); + } + + public void addResponse(int token, Response ret) { + queue.addResponse(token, ret); + } + + public ScheduledRequest waitSubscribeUnsubscribeRequest() throws InterruptedException { + return queue.waitSubscribeUnsubscribeRequest(); + } + + public ScheduledRequest waitUpdateRequest() throws InterruptedException { + return queue.waitUpdateRequest(); + } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java index 0dd8c9c3..bf922a1a 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/SchedulerQueue.java @@ -10,7 +10,7 @@ import it.unibo.arces.wot.sepa.engine.bean.SchedulerBeans; import it.unibo.arces.wot.sepa.engine.core.ResponseHandler; -public class SchedulerQueue { +class SchedulerQueue { private static final Logger logger = LogManager.getLogger(); // Tokens diff --git a/engine/src/test/resources/endpoint-blazegraph.jpar b/engine/src/test/resources/endpoint-blazegraph.jpar new file mode 100644 index 00000000..232a7283 --- /dev/null +++ b/engine/src/test/resources/endpoint-blazegraph.jpar @@ -0,0 +1,33 @@ +{ + "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, + "sparql11protocol": { + "protocol": "http", + "port": 9999, + "query": { + "path": "/blazegraph/namespace/kb/sparql", + "method": "GET", + "format": "JSON" + }, + "update": { + "path": "/blazegraph/namespace/kb/sparql", + "method": "URL_ENCODED_POST", + "format": "JSON" + } + }, + "graphs": { + "default-graph-uri": "http://default/", + "named-graph-uri": "http://default/", + "using-graph-uri": "http://default/", + "using-named-graph-uri": "http://default/" + } +} diff --git a/engine/src/test/resources/endpoint-virtuoso.jpar b/engine/src/test/resources/endpoint-virtuoso.jpar new file mode 100644 index 00000000..37dce5b8 --- /dev/null +++ b/engine/src/test/resources/endpoint-virtuoso.jpar @@ -0,0 +1,33 @@ +{ + "host": "localhost", + "oauth": { + "enable": false, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "", + "client_secret": "", + "jwt": "", + "expires": "", + "type": "" + }, + "graphs": { + "default-graph-uri": "http://default", + "named-graph-uri": "http://default", + "using-graph-uri": "http://default", + "using-named-graph-uri": "http://default" + }, + "sparql11protocol": { + "protocol": "http", + "port": 8890, + "query": { + "path": "/sparql", + "method": "GET", + "format": "JSON" + }, + "update": { + "path": "/sparql", + "method": "POST", + "format": "JSON" + } + } +} \ No newline at end of file diff --git a/engine/src/test/resources/engine-secure.jpar b/engine/src/test/resources/engine-secure.jpar index 9463d40b..6daacd8d 100644 --- a/engine/src/test/resources/engine-secure.jpar +++ b/engine/src/test/resources/engine-secure.jpar @@ -1,7 +1,8 @@ { "parameters": { "scheduler": { - "queueSize": 100 + "queueSize": 100, + "timeout" : 5000 }, "processor": { "updateTimeout": 60000, diff --git a/engine/src/test/resources/engine.jpar b/engine/src/test/resources/engine.jpar index c9beca29..74da96c4 100644 --- a/engine/src/test/resources/engine.jpar +++ b/engine/src/test/resources/engine.jpar @@ -1,7 +1,8 @@ { "parameters": { "scheduler": { - "queueSize": 100 + "queueSize": 100, + "timeout" : 5000 }, "processor": { "updateTimeout": 60000, From 5c4b16b5c399f3156dae0482daf4c575908662de Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 19 Sep 2018 17:51:02 +0200 Subject: [PATCH 54/76] Ready to go? :-) --- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 8 +- .../unibo/arces/wot/sepa/api/Publisher.java | 7 +- .../src/test/resources/sepatest-secure.jsap | 77 +------------------ .../scheduling/InternalUpdateRequest.java | 2 +- engine/src/test/resources/log4j2.xml | 4 +- 5 files changed, 13 insertions(+), 85 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 746ee676..891d7dc4 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -227,10 +227,10 @@ public void Subscribe() sync.getEvents() != subscribers.size()); } - @Test(timeout = 60000) + @Test(timeout = 5000) public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - int n = 30; + int n = 10; for (int i = 0; i < n; i++) { subscribers.add(new Subscriber("ALL", sm, sync)); @@ -319,7 +319,7 @@ public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtoc @Test(timeout = 60000) public void UpdateHeavyLoad() throws InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 10; + int n = 5; for (int i = 0; i < n; i++) { publishers.add(new Publisher("RANDOM", sm, n)); @@ -374,7 +374,7 @@ public void StressTest() throws IOException, IllegalArgumentException, SEPAProto @Test(timeout = 60000) public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 15; + int n = 5; for (int i = 0; i < n; i++) { subscribers.add(new Subscriber("ALL", sm, sync)); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java index e078b4a7..6bbed5ea 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -9,6 +9,7 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; +import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -42,12 +43,14 @@ public Publisher(String id,SEPASecurityManager sm,long n) throws SEPAPropertiesE public void run() { while(running.get() > 0) { - Response ret = client.update(provider.buildUpdateRequest(id,5000,sm)); + UpdateRequest req = provider.buildUpdateRequest(id,5000,sm); + Response ret = client.update(req); if (ret.isError()) { ErrorResponse error = (ErrorResponse) ret; logger.error(error); if (error.isTokenExpiredError()) { - client.update(provider.buildUpdateRequest(id,5000,sm)); + req = provider.buildUpdateRequest(id,5000,sm); + client.update(req); } else assertFalse(error.toString(),true); diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index bb0793b1..1aaa92f6 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -1,76 +1 @@ -{ - "host": "localhost", - "oauth": { - "enable": true, - "register": "https://localhost:8443/oauth/register", - "tokenRequest": "https://localhost:8443/oauth/token" - }, - "sparql11protocol": { - "protocol": "https", - "port": 8443, - "query": { - "path": "/secure/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/secure/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "wss", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/secure/subscribe" - } - } - }, - "graphs": { - "default-graph-uri": "http://sepatest", - "named-graph-uri": "http://sepatest", - "using-graph-uri": "http://sepatest", - "using-named-graph-uri": "http://sepatest" - }, - "namespaces": { - "sepa": "http://wot.arces.unibo.it/sepa#", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - }, - "updates": { - "DELETE_ALL" : { - "sparql" : "DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }" - }, - "VAIMEE": { - "sparql": "DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}" - }, - "RANDOM": { - "sparql": "DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" - }, - "RANDOM1": { - "sparql": "DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" - } - }, - "queries": { - "VAIMEE": { - "sparql": "SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}" - }, - "ALL": { - "sparql": "SELECT * WHERE {GRAPH {?x ?y ?z}}" - }, - "RANDOM": { - "sparql": "SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}" - }, - "RANDOM1": { - "sparql": "SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}" - }, - "COUNT": { - "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" - } - } -} +{"host":"localhost","oauth":{"enable":true,"register":"https://localhost:8443/oauth/register","tokenRequest":"https://localhost:8443/oauth/token","client_id":"0IWFPpcBdkiZDqsvd2g/hjES9a3bvWhES7ieo/oH1nJx/lBURPHmu/uw9rSqs52M","client_secret":"kMEeuqq/tj8yILnL4u0rNIJAPcdkLTfx6yIt4wOoV1F3dWmZkG8NJUJKzyyMoiat","jwt":"xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oyeoFFePUX8MQIuqd3TIcjOhoPgYWd0E+L/EN5wItL5/n78pX/8mVZcpxdyNNqr3bVvrCi0I84mIAefwQ0GyPxFhUHu9PtVNQnXchZuFgppX/YDtOesZSxfIoffUpHFPBY3u4FRIYwpSZX96Knnp0J22RQm+0l8yobik3z6jftw0jbF5+/YC6PnfZT3Wzb6PRJPuVkDzpo+BTC9eKx87GEj8VjtfXjbYRTeZNumD+59wL5kV/OrntNqNQD+IzAYoIZk4rlRbNouNnvT0laEhV012tSD1uAfNUxAlZjSbSMTp5bPNp7PoutMr5q6zPYfAC1PTKnVdkD1CDNqZnhB838WDeISfVzXsf7dsZ0+SkNPtx2kMUYCOYsxNJxyzza3lmaCuvxfnDT3g5F41/p/zX1tXYy6emVfdOWSkJNm1z0FJB/ZIUES0WAA5UEM3kejND++vvIQr38ar72HdFzRvP2V29CsaE5PMRRRZIE5ru9Zwgdb5lfMdwDi4sZkQdNRGHiOfRCT9D92mFVps6s6kv7HKojx05R9WKMDG8bEmSgMYSYQlQzLm93Ardw/hpDoB1/DfNRxbc/GVNZEVOoRVMye8/vICZtxvVeKmu4QawWKSBtrXelWUT8AHTG6v/c88pZjtJWDzy6YIIXLDQ2eJPu30mt3gLfS2ukIV4Tl5Oqu3T1IIghmNgek8vwWNeuG/JGeKrfUp6X6mMH9hdmj5+naOIr8V5rUKCjXnlWLAsrGdOvV8vuYYbx2IFQScZQJD/sTKj3gs6yeYpOwQ2iEs9asA=","expires":"9SN2d0sZEIN16Kts7LxUuQ==","type":"XPrHEX2xHy+5IuXHPHigMw=="},"sparql11protocol":{"protocol":"https","port":8443,"query":{"path":"/secure/query","method":"POST","format":"JSON"},"update":{"path":"/secure/update","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"wss","availableProtocols":{"ws":{"port":9000,"path":"/subscribe"},"wss":{"port":9443,"path":"/secure/subscribe"}}},"namespaces":{"sepa":"http://wot.arces.unibo.it/sepa#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},"updates":{"DELETE_ALL":{"sparql":"DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }"},"VAIMEE":{"sparql":"DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}"},"RANDOM":{"sparql":"DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}"},"RANDOM1":{"sparql":"DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}"}},"queries":{"VAIMEE":{"sparql":"SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}"},"ALL":{"sparql":"SELECT * WHERE {GRAPH {?x ?y ?z}}"},"RANDOM":{"sparql":"SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}"},"RANDOM1":{"sparql":"SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}"},"COUNT":{"sparql":"SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}"}}} \ No newline at end of file diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java index 2e599110..a1b22685 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/InternalUpdateRequest.java @@ -8,6 +8,6 @@ public InternalUpdateRequest(String sparql, String defaultGraphUri, String named @Override public String toString() { - return "*UPDATE* "+sparql + " DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">"; + return "*UPDATE* "+sparql + " [[DEFAULT GRAPH URI: <"+defaultGraphUri + "> NAMED GRAPH URI: <" + namedGraphUri+">]]"; } } diff --git a/engine/src/test/resources/log4j2.xml b/engine/src/test/resources/log4j2.xml index 639d3c4c..ab9391a7 100644 --- a/engine/src/test/resources/log4j2.xml +++ b/engine/src/test/resources/log4j2.xml @@ -26,8 +26,8 @@ ALL Integer.MAX_VALUE - - + + From 059b607e87ee1b524fe5f8d96ef820775a58fca5 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 20 Sep 2018 15:44:44 +0200 Subject: [PATCH 55/76] Fix travis file paths --- .travis.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 20cd901f..de97ae20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,9 +19,7 @@ jobs: - sleep 30 - cd ../.. script: - - cd client-api/target - - mvn verify -D testConfiguration=sepatest.jsap - - cd ../.. + - mvn verify - stage: integrationSecure before_script: - docker run --name blazegraph -d -p 9999:9999 nawer/blazegraph:2.1.1 @@ -34,7 +32,7 @@ jobs: - sleep 30 - cd ../.. script: - - mvn verify + - mvn verify -Dsecure=true - stage: integration-virtuoso before_script: - docker run --name my-virtuoso -p 8890:8890 -p 1111:1111 -e DBA_PASSWORD=myDbaPassword -e SPARQL_UPDATE=true -e DEFAULT_GRAPH=http://www.example.com/my-graph -d tenforce/virtuoso From 4481a751f4cb13c39861b505cf75f44bd62dfa9c Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 20 Sep 2018 15:45:21 +0200 Subject: [PATCH 56/76] Fix sepa keys loading in integrated tests --- .../unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java | 7 +++++-- .../websocket/ITWebSocketSubscriptionProtocol.java | 8 +++++++- .../sepa/commons/security/ITSEPASecurityManager.java | 10 +++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 891d7dc4..d3f1032d 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -21,6 +21,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -49,7 +50,9 @@ public static void init() throws Exception { properties = provider.getJsap(); if (properties.isSecure()) { - sm = new SEPASecurityManager("sepa.jks", "sepa2017", "sepa2017", + ClassLoader classLoader = ITSPARQL11SEProtocol.class.getClassLoader(); + File keyFile = new File(classLoader.getResource("sepa.jks").getFile()); + sm = new SEPASecurityManager(keyFile.getPath(), "sepa2017", "sepa2017", new AuthenticationProperties(properties.getFileName())); // Registration @@ -227,7 +230,7 @@ public void Subscribe() sync.getEvents() != subscribers.size()); } - @Test(timeout = 5000) + @Test(timeout = 100000) public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { int n = 10; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 46602ff1..9b25942e 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -1,9 +1,12 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; +import java.io.File; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; +import it.unibo.arces.wot.sepa.api.ITSPARQL11SEProtocol; +import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.After; @@ -40,7 +43,10 @@ public static void init() throws SEPAPropertiesException, SEPASecurityException, provider = new ConfigurationProvider(); if (provider.getJsap().isSecure()) { - sm = new SEPASecurityManager(provider.getJsap().getAuthenticationProperties()); + ClassLoader classLoader = ITSPARQL11SEProtocol.class.getClassLoader(); + File keyFile = new File(classLoader.getResource("sepa.jks").getFile()); + sm = new SEPASecurityManager(keyFile.getPath(), "sepa2017", "sepa2017", + provider.getJsap().getAuthenticationProperties()); sm.register("SEPATest"); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java index 9929f42e..71a368bb 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -1,5 +1,6 @@ package it.unibo.arces.wot.sepa.commons.security; +import it.unibo.arces.wot.sepa.api.ITSPARQL11SEProtocol; import org.junit.BeforeClass; import org.junit.Test; @@ -11,6 +12,8 @@ import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; import it.unibo.arces.wot.sepa.pattern.JSAP; +import java.io.File; + import static org.junit.Assert.*; public class ITSEPASecurityManager { @@ -23,7 +26,12 @@ public class ITSEPASecurityManager { @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { app = new ConfigurationProvider().getJsap(); - if (app.isSecure()) sm = new SEPASecurityManager(app.getAuthenticationProperties()); + if (app.isSecure()){ + ClassLoader classLoader = ITSPARQL11SEProtocol.class.getClassLoader(); + File keyFile = new File(classLoader.getResource("sepa.jks").getFile()); + sm = new SEPASecurityManager(keyFile.getPath(), "sepa2017", "sepa2017", + app.getAuthenticationProperties()); + } } @Test(timeout = 2000) From 56710c8e559948de73a7aea1dc579022f1330c85 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 20 Sep 2018 15:46:03 +0200 Subject: [PATCH 57/76] Format sepatest-secure.js --- .../src/test/resources/sepatest-secure.jsap | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index 1aaa92f6..b014f55f 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -1 +1,75 @@ -{"host":"localhost","oauth":{"enable":true,"register":"https://localhost:8443/oauth/register","tokenRequest":"https://localhost:8443/oauth/token","client_id":"0IWFPpcBdkiZDqsvd2g/hjES9a3bvWhES7ieo/oH1nJx/lBURPHmu/uw9rSqs52M","client_secret":"kMEeuqq/tj8yILnL4u0rNIJAPcdkLTfx6yIt4wOoV1F3dWmZkG8NJUJKzyyMoiat","jwt":"xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oyeoFFePUX8MQIuqd3TIcjOhoPgYWd0E+L/EN5wItL5/n78pX/8mVZcpxdyNNqr3bVvrCi0I84mIAefwQ0GyPxFhUHu9PtVNQnXchZuFgppX/YDtOesZSxfIoffUpHFPBY3u4FRIYwpSZX96Knnp0J22RQm+0l8yobik3z6jftw0jbF5+/YC6PnfZT3Wzb6PRJPuVkDzpo+BTC9eKx87GEj8VjtfXjbYRTeZNumD+59wL5kV/OrntNqNQD+IzAYoIZk4rlRbNouNnvT0laEhV012tSD1uAfNUxAlZjSbSMTp5bPNp7PoutMr5q6zPYfAC1PTKnVdkD1CDNqZnhB838WDeISfVzXsf7dsZ0+SkNPtx2kMUYCOYsxNJxyzza3lmaCuvxfnDT3g5F41/p/zX1tXYy6emVfdOWSkJNm1z0FJB/ZIUES0WAA5UEM3kejND++vvIQr38ar72HdFzRvP2V29CsaE5PMRRRZIE5ru9Zwgdb5lfMdwDi4sZkQdNRGHiOfRCT9D92mFVps6s6kv7HKojx05R9WKMDG8bEmSgMYSYQlQzLm93Ardw/hpDoB1/DfNRxbc/GVNZEVOoRVMye8/vICZtxvVeKmu4QawWKSBtrXelWUT8AHTG6v/c88pZjtJWDzy6YIIXLDQ2eJPu30mt3gLfS2ukIV4Tl5Oqu3T1IIghmNgek8vwWNeuG/JGeKrfUp6X6mMH9hdmj5+naOIr8V5rUKCjXnlWLAsrGdOvV8vuYYbx2IFQScZQJD/sTKj3gs6yeYpOwQ2iEs9asA=","expires":"9SN2d0sZEIN16Kts7LxUuQ==","type":"XPrHEX2xHy+5IuXHPHigMw=="},"sparql11protocol":{"protocol":"https","port":8443,"query":{"path":"/secure/query","method":"POST","format":"JSON"},"update":{"path":"/secure/update","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"wss","availableProtocols":{"ws":{"port":9000,"path":"/subscribe"},"wss":{"port":9443,"path":"/secure/subscribe"}}},"namespaces":{"sepa":"http://wot.arces.unibo.it/sepa#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},"updates":{"DELETE_ALL":{"sparql":"DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }"},"VAIMEE":{"sparql":"DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}"},"RANDOM":{"sparql":"DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}"},"RANDOM1":{"sparql":"DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}"}},"queries":{"VAIMEE":{"sparql":"SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}"},"ALL":{"sparql":"SELECT * WHERE {GRAPH {?x ?y ?z}}"},"RANDOM":{"sparql":"SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}"},"RANDOM1":{"sparql":"SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}"},"COUNT":{"sparql":"SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}"}}} \ No newline at end of file +{ + "host": "localhost", + "oauth": { + "enable": true, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "0IWFPpcBdkiZDqsvd2g/hjES9a3bvWhES7ieo/oH1nJx/lBURPHmu/uw9rSqs52M", + "client_secret": "kMEeuqq/tj8yILnL4u0rNIJAPcdkLTfx6yIt4wOoV1F3dWmZkG8NJUJKzyyMoiat", + "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oyeoFFePUX8MQIuqd3TIcjOhoPgYWd0E+L/EN5wItL5/n78pX/8mVZcpxdyNNqr3bVvrCi0I84mIAefwQ0GyPxFhUHu9PtVNQnXchZuFgppX/YDtOesZSxfIoffUpHFPBY3u4FRIYwpSZX96Knnp0J22RQm+0l8yobik3z6jftw0jbF5+/YC6PnfZT3Wzb6PRJPuVkDzpo+BTC9eKx87GEj8VjtfXjbYRTeZNumD+59wL5kV/OrntNqNQD+IzAYoIZk4rlRbNouNnvT0laEhV012tSD1uAfNUxAlZjSbSMTp5bPNp7PoutMr5q6zPYfAC1PTKnVdkD1CDNqZnhB838WDeISfVzXsf7dsZ0+SkNPtx2kMUYCOYsxNJxyzza3lmaCuvxfnDT3g5F41/p/zX1tXYy6emVfdOWSkJNm1z0FJB/ZIUES0WAA5UEM3kejND++vvIQr38ar72HdFzRvP2V29CsaE5PMRRRZIE5ru9Zwgdb5lfMdwDi4sZkQdNRGHiOfRCT9D92mFVps6s6kv7HKojx05R9WKMDG8bEmSgMYSYQlQzLm93Ardw/hpDoB1/DfNRxbc/GVNZEVOoRVMye8/vICZtxvVeKmu4QawWKSBtrXelWUT8AHTG6v/c88pZjtJWDzy6YIIXLDQ2eJPu30mt3gLfS2ukIV4Tl5Oqu3T1IIghmNgek8vwWNeuG/JGeKrfUp6X6mMH9hdmj5+naOIr8V5rUKCjXnlWLAsrGdOvV8vuYYbx2IFQScZQJD/sTKj3gs6yeYpOwQ2iEs9asA=", + "expires": "9SN2d0sZEIN16Kts7LxUuQ==", + "type": "XPrHEX2xHy+5IuXHPHigMw==" + }, + "sparql11protocol": { + "protocol": "https", + "port": 8443, + "query": { + "path": "/secure/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/secure/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "wss", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "updates": { + "DELETE_ALL": { + "sparql": "DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }" + }, + "VAIMEE": { + "sparql": "DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}" + }, + "RANDOM": { + "sparql": "DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + }, + "RANDOM1": { + "sparql": "DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + } + }, + "queries": { + "VAIMEE": { + "sparql": "SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}" + }, + "ALL": { + "sparql": "SELECT * WHERE {GRAPH {?x ?y ?z}}" + }, + "RANDOM": { + "sparql": "SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}" + }, + "RANDOM1": { + "sparql": "SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}" + }, + "COUNT": { + "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" + } + } +} \ No newline at end of file From cd132409330b0c4145692321faf082d2e881a855 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Fri, 21 Sep 2018 13:00:31 +0200 Subject: [PATCH 58/76] Testing testing --- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 46 +---------- .../src/test/resources/sepatest-secure.jsap | 76 ++++++++++++++++++- 2 files changed, 78 insertions(+), 44 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 891d7dc4..6255c967 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -230,7 +230,7 @@ public void Subscribe() @Test(timeout = 5000) public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - int n = 10; + int n = 20; for (int i = 0; i < n; i++) { subscribers.add(new Subscriber("ALL", sm, sync)); @@ -331,50 +331,10 @@ public void UpdateHeavyLoad() throws InterruptedException, SEPAPropertiesExcepti for (Publisher pub : publishers) pub.join(); } - /* To be used for long lasting test (30 min)*/ - //@Test(timeout = 1800000) - public void StressTest() throws IOException, IllegalArgumentException, SEPAProtocolException, - InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 50; - - for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("ALL", sm, sync)); - subscribers.add(new Subscriber("RANDOM", sm, sync)); - subscribers.add(new Subscriber("RANDOM1", sm, sync)); - } - - - for (Subscriber sub : subscribers) sub.start(); - - sync.waitSubscribes(subscribers.size()); - sync.waitEvents(subscribers.size()); - - assertFalse("Events:" + sync.getEvents() + "(" + subscribers.size() + ")", sync.getEvents() != subscribers.size()); - - int events = 4 * n * n * n; - - while (true) { - publishers.clear(); - sync.reset(); - - for (int i = 0; i < n; i++) { - publishers.add(new Publisher("RANDOM", sm, n)); - publishers.add(new Publisher("RANDOM1", sm, n)); - } - - for (Publisher pub : publishers) pub.start(); - for (Publisher pub : publishers) pub.join(); - - sync.waitEvents(events); - - assertFalse("Events:" + sync.getEvents() + "(" + events + ")", sync.getEvents() != events); - } - } - - @Test(timeout = 60000) + @Test(timeout = 30000) public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 5; + int n = 10; for (int i = 0; i < n; i++) { subscribers.add(new Subscriber("ALL", sm, sync)); diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index 1aaa92f6..2f0bfade 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -1 +1,75 @@ -{"host":"localhost","oauth":{"enable":true,"register":"https://localhost:8443/oauth/register","tokenRequest":"https://localhost:8443/oauth/token","client_id":"0IWFPpcBdkiZDqsvd2g/hjES9a3bvWhES7ieo/oH1nJx/lBURPHmu/uw9rSqs52M","client_secret":"kMEeuqq/tj8yILnL4u0rNIJAPcdkLTfx6yIt4wOoV1F3dWmZkG8NJUJKzyyMoiat","jwt":"xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oyeoFFePUX8MQIuqd3TIcjOhoPgYWd0E+L/EN5wItL5/n78pX/8mVZcpxdyNNqr3bVvrCi0I84mIAefwQ0GyPxFhUHu9PtVNQnXchZuFgppX/YDtOesZSxfIoffUpHFPBY3u4FRIYwpSZX96Knnp0J22RQm+0l8yobik3z6jftw0jbF5+/YC6PnfZT3Wzb6PRJPuVkDzpo+BTC9eKx87GEj8VjtfXjbYRTeZNumD+59wL5kV/OrntNqNQD+IzAYoIZk4rlRbNouNnvT0laEhV012tSD1uAfNUxAlZjSbSMTp5bPNp7PoutMr5q6zPYfAC1PTKnVdkD1CDNqZnhB838WDeISfVzXsf7dsZ0+SkNPtx2kMUYCOYsxNJxyzza3lmaCuvxfnDT3g5F41/p/zX1tXYy6emVfdOWSkJNm1z0FJB/ZIUES0WAA5UEM3kejND++vvIQr38ar72HdFzRvP2V29CsaE5PMRRRZIE5ru9Zwgdb5lfMdwDi4sZkQdNRGHiOfRCT9D92mFVps6s6kv7HKojx05R9WKMDG8bEmSgMYSYQlQzLm93Ardw/hpDoB1/DfNRxbc/GVNZEVOoRVMye8/vICZtxvVeKmu4QawWKSBtrXelWUT8AHTG6v/c88pZjtJWDzy6YIIXLDQ2eJPu30mt3gLfS2ukIV4Tl5Oqu3T1IIghmNgek8vwWNeuG/JGeKrfUp6X6mMH9hdmj5+naOIr8V5rUKCjXnlWLAsrGdOvV8vuYYbx2IFQScZQJD/sTKj3gs6yeYpOwQ2iEs9asA=","expires":"9SN2d0sZEIN16Kts7LxUuQ==","type":"XPrHEX2xHy+5IuXHPHigMw=="},"sparql11protocol":{"protocol":"https","port":8443,"query":{"path":"/secure/query","method":"POST","format":"JSON"},"update":{"path":"/secure/update","method":"POST","format":"JSON"}},"sparql11seprotocol":{"protocol":"wss","availableProtocols":{"ws":{"port":9000,"path":"/subscribe"},"wss":{"port":9443,"path":"/secure/subscribe"}}},"namespaces":{"sepa":"http://wot.arces.unibo.it/sepa#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#"},"updates":{"DELETE_ALL":{"sparql":"DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }"},"VAIMEE":{"sparql":"DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}"},"RANDOM":{"sparql":"DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}"},"RANDOM1":{"sparql":"DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}"}},"queries":{"VAIMEE":{"sparql":"SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}"},"ALL":{"sparql":"SELECT * WHERE {GRAPH {?x ?y ?z}}"},"RANDOM":{"sparql":"SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}"},"RANDOM1":{"sparql":"SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}"},"COUNT":{"sparql":"SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}"}}} \ No newline at end of file +{ + "host": "localhost", + "oauth": { + "enable": true, + "register": "https://localhost:8443/oauth/register", + "tokenRequest": "https://localhost:8443/oauth/token", + "client_id": "0IWFPpcBdkiZDqsvd2g/hjES9a3bvWhES7ieo/oH1nJx/lBURPHmu/uw9rSqs52M", + "client_secret": "kMEeuqq/tj8yILnL4u0rNIJAPcdkLTfx6yIt4wOoV1F3dWmZkG8NJUJKzyyMoiat", + "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oyeoFFePUX8MQIuqd3TIcjOhoPgYWd0E+L/EN5wItL5/n78pX/8mVZcpxdyNNqr3bVvrCi0I84mIAefwQ0GyPxFhUHu9PtVNQnXchZuFgppX/YDtOesZSxfIoffUpHFPBY3u4FRIYwpSZX96Knnp0J22RQm+0l8yobik3z6jftw0jbF5+/YC6PnfZT3Wzb6PRJPuVkDzpo+BTC9eKx87GEj8VjtfXjbYRTeZNumD+59wL5kV/OrntNqNQD+IzAYoIZk4rlRbNouNnvT0laEhV012tSD1uAfNUxAlZjSbSMTp5bPNp7PoutMr5q6zPYfAC1PTKnVdkD1CDNqZnhB838WDeISfVzXsf7dsZ0+SkNPtx2kMUYCOYsxNJxyzza3lmaCuvxfnDT3g5F41/p/zX1tXYy6emVfdOWSkJNm1z0FJB/ZIUES0WAA5UEM3kejND++vvIQr38ar72HdFzRvP2V29CsaE5PMRRRZIE5ru9Zwgdb5lfMdwDi4sZkQdNRGHiOfRCT9D92mFVps6s6kv7HKojx05R9WKMDG8bEmSgMYSYQlQzLm93Ardw/hpDoB1/DfNRxbc/GVNZEVOoRVMye8/vICZtxvVeKmu4QawWKSBtrXelWUT8AHTG6v/c88pZjtJWDzy6YIIXLDQ2eJPu30mt3gLfS2ukIV4Tl5Oqu3T1IIghmNgek8vwWNeuG/JGeKrfUp6X6mMH9hdmj5+naOIr8V5rUKCjXnlWLAsrGdOvV8vuYYbx2IFQScZQJD/sTKj3gs6yeYpOwQ2iEs9asA=", + "expires": "9SN2d0sZEIN16Kts7LxUuQ==", + "type": "XPrHEX2xHy+5IuXHPHigMw==" + }, + "sparql11protocol": { + "protocol": "https", + "port": 8443, + "query": { + "path": "/secure/query", + "method": "POST", + "format": "JSON" + }, + "update": { + "path": "/secure/update", + "method": "POST", + "format": "JSON" + } + }, + "sparql11seprotocol": { + "protocol": "wss", + "availableProtocols": { + "ws": { + "port": 9000, + "path": "/subscribe" + }, + "wss": { + "port": 9443, + "path": "/secure/subscribe" + } + } + }, + "namespaces": { + "sepa": "http://wot.arces.unibo.it/sepa#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" + }, + "updates": { + "DELETE_ALL": { + "sparql": "DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }" + }, + "VAIMEE": { + "sparql": "DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}" + }, + "RANDOM": { + "sparql": "DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + }, + "RANDOM1": { + "sparql": "DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" + } + }, + "queries": { + "VAIMEE": { + "sparql": "SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}" + }, + "ALL": { + "sparql": "SELECT * WHERE {GRAPH {?x ?y ?z}}" + }, + "RANDOM": { + "sparql": "SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}" + }, + "RANDOM1": { + "sparql": "SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}" + }, + "COUNT": { + "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" + } + } +} \ No newline at end of file From 6f5c08ba2126029fb1f2ab5bc005e1ac055da494 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Tue, 25 Sep 2018 18:40:22 +0200 Subject: [PATCH 59/76] Help me please, Cris! --- client-api/pom.xml | 7 + .../wot/sepa/api/ISubscriptionHandler.java | 2 +- .../wot/sepa/api/SPARQL11SEProtocol.java | 11 +- .../wot/sepa/api/SubscriptionProtocol.java | 22 +- ...etClient.java => JavaWebsocketClient.java} | 34 ++- .../websocket/TyrusWebsocketClient.java | 130 ++++++++ .../WebsocketSubscriptionProtocol.java | 282 ++++++++++++++---- .../WebsocketSubscriptionProtocol2.java | 155 ++++++++++ .../commons/protocol/SPARQL11Protocol.java | 40 ++- .../security/AuthenticationProperties.java | 2 +- .../commons/security/SEPASecurityManager.java | 128 +++++--- .../arces/wot/sepa/pattern/Consumer.java | 11 +- .../arces/wot/sepa/pattern/GenericClient.java | 9 +- .../unibo/arces/wot/sepa/ConnectTester.java | 61 ++++ .../arces/wot/sepa/ConnectTesterTyrus.java | 51 ++++ .../arces/wot/sepa/{api => }/Publisher.java | 34 ++- .../arces/wot/sepa/{api => }/Subscriber.java | 76 +++-- .../java/it/unibo/arces/wot/sepa/Sync.java | 82 +++++ .../arces/wot/sepa/TyrusWebsocketClient.java | 27 ++ .../wot/sepa/api/ITSPARQL11SEProtocol.java | 37 +-- .../it/unibo/arces/wot/sepa/api/Sync.java | 97 ------ .../websocket/ITSEPAWebsocketClient.java | 12 +- .../websocket/ITTyrusWebSocketClient.java | 90 ++++++ .../protocol/websocket/ITWebSocketClient.java | 89 ------ .../ITWebSocketSubscriptionProtocol.java | 89 +++--- client-api/src/test/resources/.gitignore | 1 + client-api/src/test/resources/log4j2.xml | 4 +- .../it/arces/wot/sepa/engine/gates/Gate.java | 2 +- .../wot/sepa/engine/gates/WebsocketGate.java | 1 + .../dependability/AuthorizationManager.java | 2 +- .../engine/processing/SPARQLAnalyzer.java | 5 - .../processing/subscriptions/SPUManager.java | 2 +- .../http/handler/RegisterHandler.java | 2 +- .../protocol/websocket/WebsocketServer.java | 8 +- .../wot/sepa/engine/scheduling/Scheduler.java | 64 ++-- engine/src/main/resources/log4j2.xml | 4 +- .../engine/processing/SPARQLAnalyzerTest.java | 7 - engine/src/test/resources/log4j2.xml | 25 +- 38 files changed, 1188 insertions(+), 517 deletions(-) rename client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/{SEPAWebsocketClient.java => JavaWebsocketClient.java} (81%) create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/TyrusWebsocketClient.java create mode 100644 client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol2.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java rename client-api/src/test/java/it/unibo/arces/wot/sepa/{api => }/Publisher.java (71%) rename client-api/src/test/java/it/unibo/arces/wot/sepa/{api => }/Subscriber.java (67%) create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java create mode 100644 client-api/src/test/resources/.gitignore diff --git a/client-api/pom.xml b/client-api/pom.xml index 929ed78a..cd7c07d4 100644 --- a/client-api/pom.xml +++ b/client-api/pom.xml @@ -50,6 +50,13 @@ 4.4.6 + + + org.glassfish.tyrus.bundles + tyrus-standalone-client + 1.12 + diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java index c051b31c..6d7d3702 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/ISubscriptionHandler.java @@ -36,7 +36,7 @@ public interface ISubscriptionHandler { /** * This method is called after the connection with SEPA is lost. - * Notice that it is also called even after {@link SEPAWebsocketClient#close()} + * Notice that it is also called even after {@link JavaWebsocketClient#close()} * is used. */ void onBrokenConnection(); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java index e82fae77..ccd4d033 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SPARQL11SEProtocol.java @@ -31,6 +31,7 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; /** * This class implements the SPARQL 1.1 Secure event protocol with SPARQL 1.1 @@ -47,7 +48,13 @@ public class SPARQL11SEProtocol extends SPARQL11Protocol { private final SubscriptionProtocol subscriptionProtocol; public SPARQL11SEProtocol(SubscriptionProtocol protocol) throws SEPAProtocolException { - super(protocol.getSecurityManager()); + super(null); + + this.subscriptionProtocol = protocol; + } + + public SPARQL11SEProtocol(SubscriptionProtocol protocol,SEPASecurityManager sm) throws SEPAProtocolException { + super(sm); this.subscriptionProtocol = protocol; } @@ -95,7 +102,7 @@ public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException * @throws IOException */ @Override - public void close() { + public void close() throws IOException { super.close(); subscriptionProtocol.close(); } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java index b8dc5c69..ea7988c5 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java @@ -1,28 +1,22 @@ package it.unibo.arces.wot.sepa.api; import java.io.Closeable; +import java.io.IOException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -public abstract class SubscriptionProtocol implements Closeable{ - protected final ISubscriptionHandler handler; - protected final SEPASecurityManager sm; +public interface SubscriptionProtocol extends Closeable { + public void setHandler(ISubscriptionHandler handler); - public SubscriptionProtocol(ISubscriptionHandler handler,SEPASecurityManager sm) { - this.handler = handler; - this.sm = sm; - } + public void enableSecurity(SEPASecurityManager sm) throws SEPASecurityException; - public abstract void close(); + public void close() throws IOException; - public abstract void subscribe(SubscribeRequest request) throws SEPAProtocolException; + public void subscribe(SubscribeRequest request) throws SEPAProtocolException; - public abstract void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException; - - public final SEPASecurityManager getSecurityManager() { - return sm; - } + public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException; } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/SEPAWebsocketClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/JavaWebsocketClient.java similarity index 81% rename from client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/SEPAWebsocketClient.java rename to client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/JavaWebsocketClient.java index 6e753250..91a4104d 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/SEPAWebsocketClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/JavaWebsocketClient.java @@ -15,20 +15,19 @@ import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; -public class SEPAWebsocketClient extends WebSocketClient { +public class JavaWebsocketClient extends WebSocketClient { protected final Logger logger = LogManager.getLogger(); private final ISubscriptionHandler handler; - - public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler, Socket secure) { + + public JavaWebsocketClient(URI wsUrl, ISubscriptionHandler handler, Socket secure) { super(wsUrl); this.handler = handler; - setSocket(secure); } - public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { + public JavaWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { super(wsUrl); this.handler = handler; @@ -36,13 +35,13 @@ public SEPAWebsocketClient(URI wsUrl, ISubscriptionHandler handler) { @Override public void onOpen(ServerHandshake handshakedata) { - logger.debug("@onOpen: "+handshakedata.getHttpStatusMessage()); + logger.debug("@onOpen: " + handshakedata.getHttpStatusMessage()); } @Override public void onClose(int code, String reason, boolean remote) { logger.debug("@onClose code:" + code + " reason:" + reason + " remote:" + remote); - + try { handler.onBrokenConnection(); } catch (Exception e) { @@ -71,13 +70,15 @@ public void onMessage(String message) { JsonObject jsonMessage = null; try { jsonMessage = new JsonParser().parse(message).getAsJsonObject(); - } catch (IllegalStateException e) { + } catch (Exception e) { logger.error(e.getMessage()); return; } if (jsonMessage.has("notification")) { JsonObject notification = jsonMessage.get("notification").getAsJsonObject(); + + // Subscribe if (notification.get("sequence").getAsInt() == 0) { String spuid = notification.get("spuid").getAsString(); String alias = null; @@ -90,24 +91,25 @@ public void onMessage(String message) { return; } } + + // Event try { handler.onSemanticEvent(new Notification(jsonMessage)); } catch (Exception e) { logger.error("Handler is null " + e.getMessage()); } } else if (jsonMessage.has("error")) { - try{ - handler.onError(new ErrorResponse(jsonMessage.get("status_code").getAsInt(),jsonMessage.get("error").getAsString(),jsonMessage.get("error_description").getAsString())); - } - catch(Exception e) { - logger.error("Handler is null "+e.getMessage()); + try { + handler.onError(new ErrorResponse(jsonMessage.get("status_code").getAsInt(), + jsonMessage.get("error").getAsString(), jsonMessage.get("error_description").getAsString())); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); } } else if (jsonMessage.has("unsubscribed")) { try { handler.onUnsubscribe(jsonMessage.get("unsubscribed").getAsJsonObject().get("spuid").getAsString()); - } - catch(Exception e) { - logger.error("Handler is null "+e.getMessage()); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); } } else logger.error("Unknown message: " + message); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/TyrusWebsocketClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/TyrusWebsocketClient.java new file mode 100644 index 00000000..93f043a1 --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/TyrusWebsocketClient.java @@ -0,0 +1,130 @@ +package it.unibo.arces.wot.sepa.api.protocols.websocket; + +import javax.net.ssl.SSLContext; +import javax.websocket.CloseReason; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.MessageHandler; +import javax.websocket.Session; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.ClientProperties; +import org.glassfish.tyrus.client.SslEngineConfigurator; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; + +public class TyrusWebsocketClient extends Endpoint { + protected Logger logger = LogManager.getLogger(); + + protected final ISubscriptionHandler handler; + protected final ClientManager client; + + public TyrusWebsocketClient(ISubscriptionHandler handler) { + this.handler = handler; + + client = ClientManager.createClient(); + } + + public TyrusWebsocketClient(ISubscriptionHandler handler, SSLContext sslContext) { + this.handler = handler; + + client = ClientManager.createClient(); + SslEngineConfigurator config = new SslEngineConfigurator(sslContext); + config.setHostVerificationEnabled(false); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, config); + } + + @Override + public void onOpen(Session session, EndpointConfig arg1) { + logger.info("onOpen session: " + session.getId()); + + session.addMessageHandler(String.class, new MessageHandler.Whole() { + @Override + public void onMessage(String message) { + logger.debug("@onMessage: " + message); + + // Parse message + JsonObject jsonMessage = null; + try { + jsonMessage = new JsonParser().parse(message).getAsJsonObject(); + } catch (Exception e) { + logger.error(e.getMessage()); + return; + } + + if (jsonMessage.has("notification")) { + JsonObject notification = jsonMessage.get("notification").getAsJsonObject(); + + // Subscribe + if (notification.get("sequence").getAsInt() == 0) { + String spuid = notification.get("spuid").getAsString(); + String alias = null; + if (notification.has("alias")) + alias = notification.get("alias").getAsString(); + try { + handler.onSubscribe(spuid, alias); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + return; + } + } + + // Event + try { + handler.onSemanticEvent(new Notification(jsonMessage)); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } else if (jsonMessage.has("error")) { + try { + handler.onError(new ErrorResponse(jsonMessage.get("status_code").getAsInt(), + jsonMessage.get("error").getAsString(), jsonMessage.get("error_description").getAsString())); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } else if (jsonMessage.has("unsubscribed")) { + try { + handler.onUnsubscribe(jsonMessage.get("unsubscribed").getAsJsonObject().get("spuid").getAsString()); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } else + logger.error("Unknown message: " + message); + } + }); + } + + @Override + public void onClose(Session session, CloseReason closeReason) { + try { + handler.onBrokenConnection(); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } + + @Override + public void onError(Session session, Throwable thr) { + ErrorResponse error = new ErrorResponse(500, "Exception", thr.getMessage()); + + logger.debug("@onError: " + error); + + try { + handler.onError(error); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + try { + handler.onError(error); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java index 470a23ec..d085c8a7 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol.java @@ -1,10 +1,24 @@ package it.unibo.arces.wot.sepa.api.protocols.websocket; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import javax.websocket.CloseReason; +import javax.websocket.DeploymentException; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.MessageHandler; +import javax.websocket.Session; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.ClientProperties; +import org.glassfish.tyrus.client.SslEngineConfigurator; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; @@ -12,94 +26,244 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; +import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -public class WebsocketSubscriptionProtocol extends SubscriptionProtocol { +public class WebsocketSubscriptionProtocol extends Endpoint implements SubscriptionProtocol { protected final Logger logger = LogManager.getLogger(); - private final SEPAWebsocketClient client; + protected final String host; + protected int port = -1; + protected final String path; - public WebsocketSubscriptionProtocol(String host, int port, String path, SEPASecurityManager sm, - ISubscriptionHandler handler) throws SEPAProtocolException, SEPASecurityException { - super(handler,sm); + protected ISubscriptionHandler handler = null; + protected SEPASecurityManager sm = null; - try { - client = new SEPAWebsocketClient(new URI("wss://" + host + ":" + port + path), handler, sm.getSSLSocket()); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } - } + protected final ClientManager client; + protected Session session; + protected String subscribeRequest; - public WebsocketSubscriptionProtocol(String host, String path, SEPASecurityManager sm, ISubscriptionHandler handler) - throws SEPAProtocolException, SEPASecurityException { - super(handler,sm); + public WebsocketSubscriptionProtocol(String host, int port, String path) { + this.host = host; + this.port = port; + this.path = path; - try { - client = new SEPAWebsocketClient(new URI("wss://" + host + path), handler, sm.getSSLSocket()); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } + client = ClientManager.createClient(); } - public WebsocketSubscriptionProtocol(String host, int port, String path, ISubscriptionHandler handler) - throws SEPAProtocolException { - super(handler,null); - - try { - client = new SEPAWebsocketClient(new URI("ws://" + host + ":" + port + path), handler); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } - } + public WebsocketSubscriptionProtocol(String host, String path) { + this.host = host; + this.path = path; - public WebsocketSubscriptionProtocol(String host, String path, ISubscriptionHandler handler) - throws SEPAProtocolException { - super(handler,null); - - try { - client = new SEPAWebsocketClient(new URI("ws://" + host + path), handler); - } catch (URISyntaxException e) { - throw new SEPAProtocolException(e); - } + client = ClientManager.createClient(); } @Override public void subscribe(SubscribeRequest request) throws SEPAProtocolException { - logger.trace("@subscribe: "+request); - if (!client.isOpen()) { - logger.debug("connectBlocking..."); + logger.trace("@subscribe: " + request); + + // Check is socket is open + if (session != null) + if (session.isOpen()) { + try { + session.getBasicRemote().sendText(request.toString()); + return; + } catch (IOException e) { + throw new SEPAProtocolException(e); + } + } + + // Connect + URI url = null; + String scheme = "ws://"; + if (sm != null) + scheme = "wss://"; + if (port == -1) try { - if(client.connectBlocking()) logger.debug("Connected");; - } catch (InterruptedException e1) { - throw new SEPAProtocolException(e1); + url = new URI(scheme + host + path); + } catch (URISyntaxException e) { + logger.error(e.getMessage()); + throw new SEPAProtocolException(e); + } + else + try { + url = new URI(scheme + host + ":" + port + path); + } catch (URISyntaxException e) { + logger.error(e.getMessage()); + throw new SEPAProtocolException(e); + } + + // Attempt up to X times to connect + int retries = 5; + while (true) { + try { + subscribeRequest = request.toString(); + + logger.info("Connect to: " + url); + + client.connectToServer(this, url); + + return; + + } catch (DeploymentException | IOException e) { + logger.error(e.getMessage()+" Retries: "+retries); + retries--; + if (retries == 0) throw new SEPAProtocolException(e); + + try { + Thread.sleep(100); + } catch (InterruptedException e1) { + throw new SEPAProtocolException(e1); + } } } - + + } + + @Override + public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { + logger.debug("@unsubscribe: " + request); + + if (session != null) + try { + session.getBasicRemote().sendText(request.toString()); + } catch (IOException e) { + logger.error(e.getMessage()); + throw new SEPAProtocolException(e); + } + } + + @Override + public void close() throws IOException { + logger.trace("Close"); + if (session != null) + session.close(); + } + + @Override + public void setHandler(ISubscriptionHandler handler) { + if (handler == null) + throw new IllegalArgumentException("Handler is null"); + + this.handler = handler; + } + + @Override + public void enableSecurity(SEPASecurityManager sm) throws SEPASecurityException { + if (sm == null) + throw new IllegalArgumentException("Security manager is null"); + + this.sm = sm; + + SslEngineConfigurator config = new SslEngineConfigurator(sm.getSSLContext()); + config.setHostVerificationEnabled(false); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, config); + } + + @Override + public void onOpen(Session session, EndpointConfig config) { + logger.info("onOpen session: " + session.getId()); + + this.session = session; + + session.addMessageHandler(String.class, new MessageHandler.Whole() { + @Override + public void onMessage(String message) { + logger.trace("@onMessage: " + message); + + // Parse message + JsonObject jsonMessage = null; + try { + jsonMessage = new JsonParser().parse(message).getAsJsonObject(); + } catch (Exception e) { + logger.error(e.getMessage()); + return; + } + + if (jsonMessage.has("notification")) { + + JsonObject notification = jsonMessage.get("notification").getAsJsonObject(); + + // Subscribe + if (notification.get("sequence").getAsInt() == 0) { + String spuid = notification.get("spuid").getAsString(); + String alias = null; + if (notification.has("alias")) + alias = notification.get("alias").getAsString(); + try { + logger.trace("onSubscribe: " + spuid + " alias: " + alias); + handler.onSubscribe(spuid, alias); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + return; + } + } + + // Event + try { + Notification notify = new Notification(jsonMessage); + logger.trace("onSemanticEvent: " + notify); + handler.onSemanticEvent(notify); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } else if (jsonMessage.has("error")) { + ErrorResponse error = new ErrorResponse(jsonMessage.get("status_code").getAsInt(), + jsonMessage.get("error").getAsString(), jsonMessage.get("error_description").getAsString()); + logger.error(error); + try { + handler.onError(error); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } else if (jsonMessage.has("unsubscribed")) { + logger.debug("unsubscribed"); + try { + handler.onUnsubscribe( + jsonMessage.get("unsubscribed").getAsJsonObject().get("spuid").getAsString()); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + } else + logger.error("Unknown message: " + message); + } + }); + try { - logger.trace("Send"); - client.send(request.toString()); - } catch (Exception e) { - logger.error("Websocket send exception: "+e.getMessage()); - throw new SEPAProtocolException(e); + session.getBasicRemote().sendText(subscribeRequest); + } catch (IOException e) { + logger.error("onOpen send subscribe request: " + e.getMessage()); } + } @Override - public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { - logger.debug("@unsubscribe: "+request); + public void onClose(Session session, CloseReason closeReason) { + logger.warn("onClose session: " + session + " reason: " + closeReason); + try { - logger.trace("Send"); - client.send(request.toString()); + handler.onBrokenConnection(); } catch (Exception e) { - logger.error("Websocket send exception: "+e.getMessage()); - throw new SEPAProtocolException(e); + logger.error("Handler is null " + e.getMessage()); } } @Override - public void close() { - logger.debug("Close"); - client.close(); + public void onError(Session session, Throwable thr) { + ErrorResponse error = new ErrorResponse(500, "Exception", thr.getMessage()); + logger.error("@onError: " + error); + + try { + handler.onError(error); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } + try { + handler.onError(error); + } catch (Exception e) { + logger.error("Handler is null " + e.getMessage()); + } } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol2.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol2.java new file mode 100644 index 00000000..92fd0a3f --- /dev/null +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/protocols/websocket/WebsocketSubscriptionProtocol2.java @@ -0,0 +1,155 @@ +package it.unibo.arces.wot.sepa.api.protocols.websocket; + +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; +import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +public class WebsocketSubscriptionProtocol2 implements SubscriptionProtocol { + public WebsocketSubscriptionProtocol2(String host, int port, String path) { + + } + + public WebsocketSubscriptionProtocol2(String host, String path) { + + } + + @Override + public void setHandler(ISubscriptionHandler handler) { + // TODO Auto-generated method stub + + } + + @Override + public void enableSecurity(SEPASecurityManager sm) throws SEPASecurityException { + // TODO Auto-generated method stub + + } + + @Override + public void close() { + // TODO Auto-generated method stub + + } + + @Override + public void subscribe(SubscribeRequest request) throws SEPAProtocolException { + // TODO Auto-generated method stub + + } + + @Override + public void unsubscribe(UnsubscribeRequest request) throws SEPAProtocolException { + // TODO Auto-generated method stub + + } + // protected final Logger logger = LogManager.getLogger(); + // + // private final SEPAWebsocketClient client; + // + // public WebsocketSubscriptionProtocol(String host, int port, String path, + // SEPASecurityManager sm, + // ISubscriptionHandler handler) throws SEPAProtocolException, + // SEPASecurityException { + // super(handler, sm); + // + // try { + // client = new SEPAWebsocketClient(new URI("wss://" + host + ":" + port + + // path), handler, sm.getSSLSocket()); + // } catch (URISyntaxException e) { + // throw new SEPAProtocolException(e); + // } + // } + // + // public WebsocketSubscriptionProtocol(String host, String path, + // SEPASecurityManager sm, ISubscriptionHandler handler) + // throws SEPAProtocolException, SEPASecurityException { + // super(handler, sm); + // + // try { + // client = new SEPAWebsocketClient(new URI("wss://" + host + path), handler, + // sm.getSSLSocket()); + // } catch (URISyntaxException e) { + // throw new SEPAProtocolException(e); + // } + // } + // + // public WebsocketSubscriptionProtocol(String host, int port, String path, + // ISubscriptionHandler handler) + // throws SEPAProtocolException { + // super(handler, null); + // + // try { + // client = new SEPAWebsocketClient(new URI("ws://" + host + ":" + port + path), + // handler); + // } catch (URISyntaxException e) { + // throw new SEPAProtocolException(e); + // } + // } + // + // public WebsocketSubscriptionProtocol(String host, String path, + // ISubscriptionHandler handler) + // throws SEPAProtocolException { + // super(handler, null); + // + // try { + // client = new SEPAWebsocketClient(new URI("ws://" + host + path), handler); + // } catch (URISyntaxException e) { + // throw new SEPAProtocolException(e); + // } + // } + // + // @Override + // public void subscribe(SubscribeRequest request) throws SEPAProtocolException + // { + // logger.trace("@subscribe: " + request); + // + // while (!client.isOpen()) { + // logger.debug("connectBlocking..."); + // try { + // if (client.connectBlocking()) + // logger.debug("Connected"); + // else + // logger.error("Client is not connected"); + // } catch (InterruptedException e1) { + // throw new SEPAProtocolException(e1); + // } + // } + // + // boolean subscribed = false; + // while (!subscribed) { + // try { + // logger.trace("Send"); + // client.send(request.toString()); + // } catch (WebsocketNotConnectedException e) { + // logger.error("Websocket not connected exception: " + e.getMessage()); + // continue; + // } + // + // subscribed = true; + // } + // } + // + // @Override + // public void unsubscribe(UnsubscribeRequest request) throws + // SEPAProtocolException { + // logger.debug("@unsubscribe: " + request); + // try { + // logger.trace("Send"); + // client.send(request.toString()); + // } catch (WebsocketNotConnectedException e) { + // logger.warn("Websocket not connected exception: " + e.getMessage()); + // throw new SEPAProtocolException(e); + // } + // } + // + // @Override + // public void close() { + // logger.debug("Close"); + // client.close(); + // } + +} diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java index e8c469c8..2abd8a2b 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/protocol/SPARQL11Protocol.java @@ -32,6 +32,7 @@ import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -91,10 +92,6 @@ public SPARQL11Protocol() { httpClient = HttpClients.createDefault(); } -// public boolean isSecure() { -// return sm != null; -// } - private Response executeRequest(HttpUriRequest req, Request request) { CloseableHttpResponse httpResponse = null; HttpEntity responseEntity = null; @@ -110,9 +107,29 @@ private Response executeRequest(HttpUriRequest req, Request request) { try { // Execute HTTP request logger.trace(req.toString() + " " + request.toString() + " (timeout: " + request.getTimeout() + " ms) "); + long start = Timings.getTime(); - httpResponse = httpClient.execute(req); + +// int retries = 5; +// while (true) { + try { + httpResponse = httpClient.execute(req); +// break; + } catch (IOException e) { + logger.error("HTTP EXECUTE: " + e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "HttpExecute",e.getMessage()); +// if (retries == 0) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "HttpExecute",e.getMessage()); + } +// retries--; +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "InterruptedException",e.getMessage()); +// } +// } + long stop = Timings.getTime(); + if (request.getClass().equals(UpdateRequest.class)) Timings.log("ENDPOINT_UPDATE_TIME", start, stop); else @@ -129,6 +146,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { // http://hc.apache.org/httpcomponents-client-4.5.x/tutorial/html/fundamentals.html#d5e279 } catch (IOException e) { + logger.error(e.getMessage()); if (e instanceof InterruptedIOException) { return new ErrorResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "InterruptedIOException",e.getMessage()); } @@ -141,12 +159,17 @@ private Response executeRequest(HttpUriRequest req, Request request) { if (e instanceof SSLException) { return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "SSLException",e.getMessage()); } + if (e instanceof ClientProtocolException) { + return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED, "ClientProtocolException",e.getMessage()); + } return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "IOException",e.getMessage()); - } finally { + } + finally { try { if (httpResponse != null) httpResponse.close(); } catch (IOException e) { + logger.error(e.getMessage()); return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR,"IOException",e.getMessage()); } @@ -160,6 +183,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { ret = new JsonParser().parse(responseBody).getAsJsonObject(); } catch(Exception e) { + logger.error(e.getMessage()); return new ErrorResponse(responseCode,"JsonParsingException",e.getMessage()+" Response body: "+responseBody); } return new ErrorResponse(ret.get("status_code").getAsInt(), ret.get("error").getAsString(),ret.get("error_description").getAsString()); @@ -173,6 +197,7 @@ private Response executeRequest(HttpUriRequest req, Request request) { ret = new JsonParser().parse(responseBody).getAsJsonObject(); } catch(JsonParseException e) { + logger.error(e.getMessage()); return new ErrorResponse(HttpStatus.SC_UNPROCESSABLE_ENTITY,"JsonParsingException", e.getMessage() +" Response body: "+responseBody); } return new QueryResponse(ret); @@ -576,11 +601,12 @@ private Response get(QueryRequest req) { } @Override - public void close() { + public void close() throws IOException { try { httpClient.close(); } catch (IOException e) { logger.error(e.getMessage()); + throw e; } } } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java index 1bf03c28..a5bf8a58 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/AuthenticationProperties.java @@ -127,7 +127,7 @@ public synchronized long getExpiringTime() { long expires = Long.decode(encryption.decrypt(getSecurityEncryptedValue("expires"))); long now = new Date().getTime(); - logger.debug("@getExpiringTime Diff:"+(expires-now)+" Now: "+now+" Expires: "+expires); + logger.trace("@getExpiringTime Diff:"+(expires-now)+" Now: "+now+" Expires: "+expires); if (expires-now < 0) return 0; return expires-now; diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java index 94c7a1ea..1ab44fe7 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/commons/security/SEPASecurityManager.java @@ -21,8 +21,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.security.KeyManagementException; import java.security.KeyStore; @@ -40,6 +42,7 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpStatus; +import org.apache.http.ParseException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; @@ -160,7 +163,7 @@ public class SEPASecurityManager implements HostnameVerifier { * * @see SSLContext */ - //private final SSLContext sslContext; + // private final SSLContext sslContext; /** * The SSLConnectionSocketFactory context. @@ -176,7 +179,7 @@ public class SEPASecurityManager implements HostnameVerifier { private final KeyManagerFactory kmfactory; private final TrustManagerFactory tmf; - + /** * Instantiates a new Security Manager. * @@ -230,14 +233,14 @@ public SEPASecurityManager(AuthenticationProperties oauthProp) throws SEPASecuri public SEPASecurityManager(String jksName, String jksPassword, String keyPassword) throws SEPASecurityException { this(jksName, jksPassword, keyPassword, null); } - + public SEPASecurityManager() throws SEPASecurityException { this("sepa.jks", "sepa2017", "sepa2017", null); } public Socket getSSLSocket() throws SEPASecurityException { SSLContext sslContext; - + try { sslContext = SSLContext.getInstance("TLSv1"); } catch (NoSuchAlgorithmException e) { @@ -248,7 +251,7 @@ public Socket getSSLSocket() throws SEPASecurityException { } catch (KeyManagementException e) { throw new SEPASecurityException(e); } - + try { return sslContext.getSocketFactory().createSocket(); } catch (IOException e) { @@ -256,9 +259,9 @@ public Socket getSSLSocket() throws SEPASecurityException { } } - public SSLContext getSSLContext() throws SEPASecurityException { + public SSLContext getSSLContext() throws SEPASecurityException { SSLContext sslContext; - + try { sslContext = SSLContext.getInstance("TLSv1"); } catch (NoSuchAlgorithmException e) { @@ -269,7 +272,7 @@ public SSLContext getSSLContext() throws SEPASecurityException { } catch (KeyManagementException e) { throw new SEPASecurityException(e); } - + return sslContext; } @@ -310,8 +313,7 @@ public synchronized Response register(String identity) throws SEPASecurityExcept if (ret.isRegistrationResponse()) { RegistrationResponse reg = (RegistrationResponse) ret; oauthProperties.setCredentials(reg.getClientId(), reg.getClientSecret()); - } - else { + } else { logger.error(ret); } @@ -320,44 +322,48 @@ public synchronized Response register(String identity) throws SEPASecurityExcept /** * Returns the Bearer authentication header - * @throws SEPAPropertiesException - * @throws SEPASecurityException + * + * @throws SEPAPropertiesException + * @throws SEPASecurityException * * @see AuthenticationProperties */ public synchronized String getAuthorizationHeader() throws SEPASecurityException, SEPAPropertiesException { - if (oauthProperties == null) return null; - - if(isTokenExpired()) { + if (oauthProperties == null) + return null; + + if (isTokenExpired()) { requestToken(); } - + return oauthProperties.getBearerAuthorizationHeader(); } - + /** - * It is used to request a new token using the "Basic" credentials stored in the AuthenticationProperties. - * When retrieved, the token is stored within the AuthenticationProperties. + * It is used to request a new token using the "Basic" credentials stored in the + * AuthenticationProperties. When retrieved, the token is stored within the + * AuthenticationProperties. * - * @return In case of success, it returns an JWTResponse. Otherwise an ErrorResponse is returned as specified in RFC6749 - * @throws SEPASecurityException - * @throws SEPAPropertiesException + * @return In case of success, it returns an JWTResponse. Otherwise an + * ErrorResponse is returned as specified in RFC6749 + * @throws SEPASecurityException + * @throws SEPAPropertiesException * @see ErrorResponse * @see JWTResponse * @see AuthenticationProperties */ - private void requestToken() throws SEPASecurityException, SEPAPropertiesException { - Response ret = requestToken(oauthProperties.getTokenRequestUrl(),oauthProperties.getBasicAuthorizationHeader()); - + private void requestToken() throws SEPASecurityException, SEPAPropertiesException { + Response ret = requestToken(oauthProperties.getTokenRequestUrl(), + oauthProperties.getBasicAuthorizationHeader()); + if (ret.isJWTResponse()) { JWTResponse jwt = (JWTResponse) ret; - + logger.debug(jwt); - + oauthProperties.setJWT(jwt); - } - else { - logger.error("requestToken@ "+new Date()+" Response: "+ret); + } else { + logger.error("requestToken@ " + new Date() + " Response: " + ret); } } @@ -383,6 +389,7 @@ private Response register(String url, String identity) { try { URI uri = new URI(url); ByteArrayEntity body = new ByteArrayEntity(new RegistrationRequest(identity).toString().getBytes("UTF-8")); + HttpPost httpRequest = new HttpPost(uri); httpRequest.setHeader("Content-Type", "application/json"); httpRequest.setHeader("Accept", "application/json"); @@ -390,35 +397,68 @@ private Response register(String url, String identity) { logger.trace(httpRequest); - response = getSSLHttpClient().execute(httpRequest); +// int retries = 5; +// while (true) { + try { + response = getSSLHttpClient().execute(httpRequest); +// break; + } catch (IOException e) { + logger.error("HTTP EXECUTE: " + e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "HttpExecute",e.getMessage()); +// if (retries == 0) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "HttpExecute",e.getMessage()); + } +// retries--; +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "InterruptedException",e.getMessage()); +// } +// } logger.debug("Response: " + response); HttpEntity entity = response.getEntity(); String jsonResponse = EntityUtils.toString(entity, Charset.forName("UTF-8")); + EntityUtils.consume(entity); + JsonObject json = new JsonParser().parse(jsonResponse).getAsJsonObject(); if (json.has("error")) { - Timings.log("REGISTER_ERROR", start, Timings.getTime()); int code = json.get("status_code").getAsInt(); String error = json.get("error").getAsString(); String description = json.get("error_description").getAsString(); ErrorResponse ret = new ErrorResponse(code, error, description); logger.error(ret); + return ret; } String id = json.get("credentials").getAsJsonObject().get("client_id").getAsString(); String secret = json.get("credentials").getAsJsonObject().get("client_secret").getAsString(); JsonElement signature = json.get("credentials").getAsJsonObject().get("signature"); + Timings.log("REGISTER", start, Timings.getTime()); + return new RegistrationResponse(id, secret, signature); - } catch (Exception e) { + } catch (URISyntaxException e) { logger.error(e.getMessage()); Timings.log("REGISTER_ERROR", start, Timings.getTime()); - return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "Exception", e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "URISyntaxException", e.getMessage()); + } catch (UnsupportedEncodingException e) { + logger.error(e.getMessage()); + Timings.log("REGISTER_ERROR", start, Timings.getTime()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "UnsupportedEncodingException", + e.getMessage()); + } catch (ParseException e) { + logger.error(e.getMessage()); + Timings.log("REGISTER_ERROR", start, Timings.getTime()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "ParseException", e.getMessage()); + } catch (IOException e) { + logger.error(e.getMessage()); + Timings.log("REGISTER_ERROR", start, Timings.getTime()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "IOException", e.getMessage()); } finally { try { if (response != null) @@ -432,7 +472,7 @@ private Response register(String url, String identity) { } private Response requestToken(String url, String authorization) { - logger.info("TOKEN_REQUEST: "+authorization); + logger.info("TOKEN_REQUEST: " + authorization); CloseableHttpResponse response = null; long start = Timings.getTime(); @@ -445,7 +485,23 @@ private Response requestToken(String url, String authorization) { httpRequest.setHeader("Accept", "application/json"); httpRequest.setHeader("Authorization", authorization); - response = getSSLHttpClient().execute(httpRequest); +// int retries = 5; +// while (true) { + try { + response = getSSLHttpClient().execute(httpRequest); +// break; + } catch (IOException e) { + logger.error("HTTP EXECUTE: " + e.getMessage()); + return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "HttpExecute",e.getMessage()); +// if (retries == 0) return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "HttpExecute",e.getMessage()); + } +// retries--; +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// return new ErrorResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "InterruptedException",e.getMessage()); +// } +// } logger.debug("Response: " + response); HttpEntity entity = response.getEntity(); diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java index 9ab0c99f..1026afb2 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/Consumer.java @@ -74,9 +74,11 @@ public Consumer(JSAP appProfile, String subscribeID, SEPASecurityManager sm) SubscriptionProtocol protocol = null; protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), - appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID),sm,this); + appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID)); + protocol.setHandler(this); + protocol.enableSecurity(sm); - client = new SPARQL11SEProtocol(protocol); + client = new SPARQL11SEProtocol(protocol,sm); subID = subscribeID; } @@ -107,9 +109,10 @@ public Consumer(JSAP appProfile, String subscribeID) throws SEPAProtocolExceptio SubscriptionProtocol protocol = null; protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(subscribeID), - appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID),this); + appProfile.getSubscribePort(subscribeID), appProfile.getSubscribePath(subscribeID)); + protocol.setHandler(this); - client = new SPARQL11SEProtocol(protocol); + client = new SPARQL11SEProtocol(protocol,null); subID = subscribeID; } diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java index 0c8d1a27..d2591748 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/pattern/GenericClient.java @@ -196,8 +196,8 @@ private void _subscribe(String ID, String sparql, Bindings forced, ISubscription if (!subscribedClients.containsKey(url)) { protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), - appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID),handler); - + appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID)); + protocol.setHandler(handler); client = new SPARQL11SEProtocol(protocol); } else client = subscribedClients.get(url); @@ -209,8 +209,9 @@ private void _subscribe(String ID, String sparql, Bindings forced, ISubscription if (!subscribedClients.containsKey(url)) { protocol = new WebsocketSubscriptionProtocol(appProfile.getSubscribeHost(ID), - appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID), sm,handler); - + appProfile.getSubscribePort(ID), appProfile.getSubscribePath(ID)); + protocol.setHandler(handler); + protocol.enableSecurity(sm); client = new SPARQL11SEProtocol(protocol); } else client = subscribedClients.get(url); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java new file mode 100644 index 00000000..bf0890b0 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java @@ -0,0 +1,61 @@ +package it.unibo.arces.wot.sepa; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.handshake.ServerHandshake; + +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +public class ConnectTester extends WebSocketClient { + protected Logger logger = LogManager.getLogger(); + protected String url = null; + protected final Sync sync; + + public ConnectTester(URI server,Sync s,SEPASecurityManager sm) throws URISyntaxException, SEPASecurityException { + super(server); + sync = s; + + if (sm != null) setSocket(sm.getSSLSocket()); + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + logger.debug("@onOpen: " + handshakedata.getHttpStatusMessage()); + + sync.event(); + } + + @Override + public void onMessage(String message) { + logger.debug("@onMessage: " + message); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + logger.warn("@onClose code:" + code + + " reason:" + reason + + " remote:" + remote + + " STATE: "+this.getReadyState()); +// +" isConnecting: "+isConnecting() +// +" isClosed: "+this.isClosed() +// +" isClosing: "+this.isClosing() +// +" isOpen:" + this.isOpen() +// +" isFulshAndClose: "+this.isFlushAndClose() +// +" isReuseAddr: "+this.isReuseAddr()); + + if (remote && code == -1 && getReadyState().equals(READYSTATE.NOT_YET_CONNECTED)) close(); + } + + @Override + public void onError(Exception ex) { + logger.error("@onError "+ex.getMessage()); + + //reconnect(); + } + +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java new file mode 100644 index 00000000..f40c8761 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java @@ -0,0 +1,51 @@ +package it.unibo.arces.wot.sepa; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import javax.websocket.ClientEndpoint; +import javax.websocket.CloseReason; +import javax.websocket.EndpointConfig; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@ClientEndpoint +public class ConnectTesterTyrus { + + protected Logger logger = LogManager.getLogger(); + + @OnMessage + public String onMessage(String message, Session session) { + BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in)); + try { + logger.info("Received ...." + message); + String userInput = bufferRead.readLine(); + return userInput; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @OnClose + public void onClose(Session session, CloseReason closeReason) { + logger.info(String.format("Session %s close because of %s", session.getId(), closeReason)); + } + + @OnOpen + public void onOpen(Session session, EndpointConfig arg1) { + logger.info("Connected ... " + session.getId()); + + } + + @OnError + public void onError(Session session, Throwable t) { + t.printStackTrace(); + } +} \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/Publisher.java similarity index 71% rename from client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java rename to client-api/src/test/java/it/unibo/arces/wot/sepa/Publisher.java index 6bbed5ea..9437045b 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/Publisher.java @@ -1,15 +1,14 @@ -package it.unibo.arces.wot.sepa.api; +package it.unibo.arces.wot.sepa; +import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.request.UpdateRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -43,20 +42,25 @@ public Publisher(String id,SEPASecurityManager sm,long n) throws SEPAPropertiesE public void run() { while(running.get() > 0) { - UpdateRequest req = provider.buildUpdateRequest(id,5000,sm); - Response ret = client.update(req); - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - logger.error(error); - if (error.isTokenExpiredError()) { - req = provider.buildUpdateRequest(id,5000,sm); - client.update(req); - } - else - assertFalse(error.toString(),true); - } + Response ret = client.update(provider.buildUpdateRequest(id,5000,sm)); + +// if (ret.isError()) { +// ErrorResponse error = (ErrorResponse) ret; +// logger.error(error); +// if (error.isTokenExpiredError()) { +// client.update(provider.buildUpdateRequest(id,5000,sm)); +// } +// else +// assertFalse(error.toString(),true); +// } running.set(running.get()-1); } + + try { + client.close(); + } catch (IOException e) { + logger.error(e.getMessage()); + } } public void finish() { diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/Subscriber.java similarity index 67% rename from client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java rename to client-api/src/test/java/it/unibo/arces/wot/sepa/Subscriber.java index 05de21e4..4628b586 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/Subscriber.java @@ -1,11 +1,11 @@ -package it.unibo.arces.wot.sepa.api; +package it.unibo.arces.wot.sepa; +import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; @@ -25,62 +25,66 @@ public class Subscriber extends Thread implements ISubscriptionHandler { private final String id; private final Sync sync; private String spuid; - + private AtomicBoolean subscribing = new AtomicBoolean(false); private AtomicBoolean unsubscribing = new AtomicBoolean(false); - + private static ConfigurationProvider provider; - - public Subscriber(String id,SEPASecurityManager sm, Sync sync) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { + + public Subscriber(String id, SEPASecurityManager sm, Sync sync) + throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { provider = new ConfigurationProvider(); - + this.sm = sm; this.id = id; this.sync = sync; SubscriptionProtocol protocol; if (sm != null) { - protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath(), sm, this); + protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), + provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); + protocol.enableSecurity(sm); } else { - protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath(), this); + protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), + provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); } + protocol.setHandler(this); client = new SPARQL11SEProtocol(protocol); } - + public void run() { - try { - subscribe(); - synchronized(this) { + synchronized (this) { + try { + subscribe(); wait(); - } - } catch (SEPAProtocolException | InterruptedException e) { - + } catch (SEPAProtocolException | InterruptedException e) { + return; + } } } - public void close() { + public void close() throws IOException { client.close(); interrupt(); } private void subscribe() throws SEPAProtocolException, InterruptedException { subscribing.set(true); - client.subscribe(provider.buildSubscribeRequest(id, 5000,sm)); + client.subscribe(provider.buildSubscribeRequest(id, 5000, sm)); } public void unsubscribe(String spuid) throws SEPAProtocolException, InterruptedException { unsubscribing.set(true); - client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000,sm)); + client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000, sm)); } @Override public void onSemanticEvent(Notification notify) { logger.debug("@onSemanticEvent: " + notify); - sync.event(); + if (sync != null) + sync.event(); } @Override @@ -91,19 +95,31 @@ public void onBrokenConnection() { @Override public void onError(ErrorResponse errorResponse) { logger.error("@onError: " + errorResponse); - if (errorResponse.isTokenExpiredError()) { + if (errorResponse.isTokenExpiredError()) { if (subscribing.get()) try { - client.subscribe(provider.buildSubscribeRequest(id, 5000,sm)); + logger.warn("Token is expired. Renew token and subscribe again"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + return; + } + client.subscribe(provider.buildSubscribeRequest(id, 5000, sm)); } catch (SEPAProtocolException e) { logger.error(e.getMessage()); } - else if (unsubscribing.get()) + while (unsubscribing.get()) try { - client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000,sm)); + logger.warn("Token is expired. Renew token and unsubscribe again"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + return; + } + client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000, sm)); } catch (SEPAProtocolException e) { logger.error(e.getMessage()); - } + } } } @@ -112,7 +128,8 @@ public void onSubscribe(String spuid, String alias) { logger.debug("@onSubscribe: " + spuid + " alias: " + alias); subscribing.set(false); - sync.subscribe(spuid, alias); + if (sync != null) + sync.subscribe(spuid, alias); } @Override @@ -120,6 +137,7 @@ public void onUnsubscribe(String spuid) { logger.debug("@onUnsubscribe: " + spuid); unsubscribing.set(false); - sync.unsubscribe(); + if (sync != null) + sync.unsubscribe(); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java new file mode 100644 index 00000000..ce314677 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java @@ -0,0 +1,82 @@ +package it.unibo.arces.wot.sepa; + +public class Sync { + private static long events = 0; + private static long subscribes = 0; + private static long unsubscribes = 0; + private static String spuid = null; + + public synchronized void reset() { + subscribes = 0; + events = 0; + unsubscribes = 0; + spuid = null; + } + + public long getSubscribes() { + return subscribes; + } + + public long getEvents() { + return events; + } + + public long getUnsubscribes() { + return unsubscribes; + } + + public void resetSpuid() { + spuid = null; + } + + public String getSpuid() { + return spuid; + } + + public synchronized void waitSubscribes(int total) { + while (subscribes < total) { + try { + wait(1000); + } catch (InterruptedException e) { + break; + } + } + } + + public synchronized void waitEvents(int total) { + while (events < total) { + try { + wait(1000); + } catch (InterruptedException e) { + break; + } + } + } + + public synchronized void waitUnsubscribes(int total) { + while (unsubscribes < total) { + try { + wait(1000); + } catch (InterruptedException e) { + break; + } + } + } + + public synchronized void event() { + events++; + notify(); + } + + public synchronized void unsubscribe() { + unsubscribes++; + notify(); + } + + public synchronized void subscribe(String s, String alias) { + spuid = s; + subscribes++; + notify(); + } + +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java new file mode 100644 index 00000000..fd4606eb --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java @@ -0,0 +1,27 @@ +package it.unibo.arces.wot.sepa; + +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.Session; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class TyrusWebsocketClient extends Endpoint { + protected Logger logger = LogManager.getLogger(); + + protected final Sync sync; + + public TyrusWebsocketClient(Sync s) { + sync = s; + } + + @Override + public void onOpen(Session session, EndpointConfig arg1) { + logger.info("onOpen session: "+session.getId()); + + sync.event(); + + } + +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 6255c967..82e6f89b 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -1,6 +1,9 @@ package it.unibo.arces.wot.sepa.api; import it.unibo.arces.wot.sepa.ConfigurationProvider; +import it.unibo.arces.wot.sepa.Publisher; +import it.unibo.arces.wot.sepa.Subscriber; +import it.unibo.arces.wot.sepa.Sync; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -99,7 +102,7 @@ public void endTest() throws IOException, InterruptedException { } } - @Test(timeout = 5000) + @Test(timeout = 1000) public void RegisterNotAllowed() throws SEPASecurityException, SEPAPropertiesException { if (sm != null) { Response response = sm.register(NOT_VALID_ID); @@ -107,7 +110,7 @@ public void RegisterNotAllowed() throws SEPASecurityException, SEPAPropertiesExc } } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Register() throws SEPASecurityException, SEPAPropertiesException { if (sm != null) { Response response = sm.register(VALID_ID); @@ -115,7 +118,7 @@ public void Register() throws SEPASecurityException, SEPAPropertiesException { } } - @Test(timeout = 5000) + @Test(timeout = 1000) public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { // Delete all triples Response ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); @@ -150,7 +153,7 @@ public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, I } } - @Test(timeout = 25000) + @Test(timeout = 30000) public void RequestToken() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { if (sm != null) { for (int i = 0; i < 2000; i++) { @@ -161,7 +164,7 @@ public void RequestToken() throws SEPASecurityException, SEPAPropertiesException } } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); if (ret.isError()) { @@ -173,7 +176,7 @@ public void Update() throws IOException, SEPAPropertiesException, SEPASecurityEx assertFalse(String.valueOf(ret), ret.isError()); } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.query(provider.buildQueryRequest("ALL", 5000,sm)); if (ret.isError()) { @@ -185,7 +188,7 @@ public void Query() throws IOException, SEPAPropertiesException, SEPASecurityExc assertFalse(String.valueOf(ret), ret.isError()); } - @Test(timeout = 5000) + @Test(timeout = 1000) public void UpdateAndQuery() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); @@ -210,7 +213,7 @@ public void UpdateAndQuery() assertFalse(String.valueOf(ret), ((QueryResponse) ret).getBindingsResults().size() != 1); } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { subscribers.add(new Subscriber("ALL", sm, sync)); @@ -227,10 +230,10 @@ public void Subscribe() sync.getEvents() != subscribers.size()); } - @Test(timeout = 5000) + @Test (timeout = 30000) public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - int n = 20; + int n = 30; for (int i = 0; i < n; i++) { subscribers.add(new Subscriber("ALL", sm, sync)); @@ -249,7 +252,7 @@ public void Subscribe3xN() sync.getEvents() != subscribers.size()); } - @Test(timeout = 5000) + @Test (timeout = 1000) public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { subscribers.add(new Subscriber("ALL", sm, sync)); @@ -271,7 +274,7 @@ public void Unsubscribe() sync.getUnsubscribes() != subscribers.size()); } - @Test(timeout = 5000) + @Test(timeout = 1000) public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException { @@ -290,7 +293,7 @@ public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolE assertFalse("Events:" + sync.getEvents() + "(1)", sync.getEvents() != 1); } - @Test(timeout = 60000) + @Test(timeout = 5000) public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { @@ -317,7 +320,7 @@ public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtoc sync.getEvents() != subscribers.size() + subscribers.size() * publishers.size() * publishers.size()); } - @Test(timeout = 60000) + @Test (timeout = 10000) public void UpdateHeavyLoad() throws InterruptedException, SEPAPropertiesException, SEPASecurityException { int n = 5; @@ -331,10 +334,10 @@ public void UpdateHeavyLoad() throws InterruptedException, SEPAPropertiesExcepti for (Publisher pub : publishers) pub.join(); } - @Test(timeout = 30000) + @Test(timeout = 60000) public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProtocolException, InterruptedException, SEPAPropertiesException, SEPASecurityException { - int n = 10; + int n = 5; for (int i = 0; i < n; i++) { subscribers.add(new Subscriber("ALL", sm, sync)); @@ -354,7 +357,7 @@ public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProt for (Publisher pub : publishers) pub.start(); sync.waitEvents(events); - + assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", sync.getSubscribes() != subscribers.size()); assertFalse("Events:" + sync.getEvents() + "(" + events + ")", sync.getEvents() != events); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java deleted file mode 100644 index 0a640538..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Sync.java +++ /dev/null @@ -1,97 +0,0 @@ -package it.unibo.arces.wot.sepa.api; - -import java.util.concurrent.atomic.AtomicLong; - -public class Sync { - private static final AtomicLong events = new AtomicLong(0); - private static final AtomicLong subscribes = new AtomicLong(0); - private static final AtomicLong unsubscribes = new AtomicLong(0); - private String spuid = null; - - public void reset() { - subscribes.set(0); - events.set(0); - unsubscribes.set(0); - spuid = null; - } - - public long getSubscribes() { - return subscribes.get(); - } - - public long getEvents() { - return events.get(); - } - - public long getUnsubscribes() { - return unsubscribes.get(); - } - - public void resetSpuid() { - spuid = null; - } - - public String getSpuid() { - return spuid; - } - - public void waitSubscribes(int total) { - while (subscribes.get() < total) { - synchronized (subscribes) { - try { - subscribes.wait(1000); - } catch (InterruptedException e) { - - } - } - } - } - - public void waitEvents(int total) { - while (events.get() < total) { - synchronized (events) { - try { - events.wait(1000); - } catch (InterruptedException e) { - - } - } - } - } - - public void waitUnsubscribes(int total) { - while (unsubscribes.get() < total) { - synchronized (unsubscribes) { - try { - unsubscribes.wait(1000); - } catch (InterruptedException e) { - - } - } - } - - } - - public synchronized void event() { - synchronized (events) { - events.set(events.get() + 1); - events.notify(); - } - } - - public synchronized void unsubscribe() { - synchronized (unsubscribes) { - unsubscribes.set(unsubscribes.get() + 1); - unsubscribes.notify(); - } - } - - public synchronized void subscribe(String spuid,String alias) { - synchronized (subscribes) { - this.spuid = spuid; - subscribes.set(subscribes.get() + 1); - subscribes.notify(); - } - } - -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java index 3b495764..e1569818 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java @@ -9,11 +9,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.BeforeClass; -import org.junit.Test; +//import org.junit.Test; import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.protocols.websocket.SEPAWebsocketClient; +import it.unibo.arces.wot.sepa.api.protocols.websocket.JavaWebsocketClient; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; @@ -25,7 +25,7 @@ public class ITSEPAWebsocketClient implements ISubscriptionHandler { protected static JSAP properties = null; protected static String url = null; - SEPAWebsocketClient client = null; + JavaWebsocketClient client = null; @BeforeClass public static void init() throws Exception { @@ -46,16 +46,16 @@ public static void init() throws Exception { } } - @Test(timeout = 10000) + //@Test(timeout = 10000) public void Connect() throws InterruptedException, URISyntaxException, IOException, SEPASecurityException { for (int i = 0; i < 100; i++) { if(properties.isSecure()) { SEPASecurityManager sm = new SEPASecurityManager(); - client = new SEPAWebsocketClient(new URI(url), this, + client = new JavaWebsocketClient(new URI(url), this, sm.getSSLSocket()); } else { - client = new SEPAWebsocketClient(new URI(url), this); + client = new JavaWebsocketClient(new URI(url), this); } assertFalse("Failed to connect", !client.connectBlocking()); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java new file mode 100644 index 00000000..552f2fd6 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java @@ -0,0 +1,90 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashSet; + +import javax.websocket.DeploymentException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.glassfish.tyrus.client.ClientManager; +import org.glassfish.tyrus.client.ClientProperties; +import org.glassfish.tyrus.client.SslEngineConfigurator; + +import org.junit.BeforeClass; +import org.junit.Test; + +import it.unibo.arces.wot.sepa.ConfigurationProvider; +import it.unibo.arces.wot.sepa.Sync; +import it.unibo.arces.wot.sepa.TyrusWebsocketClient; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; +import it.unibo.arces.wot.sepa.pattern.JSAP; + +import static org.junit.Assert.assertFalse; + +public class ITTyrusWebSocketClient { + protected final Logger logger = LogManager.getLogger(); + protected static JSAP properties = null; + + protected static String url = null; + protected final Sync sync = new Sync(); + + protected static SEPASecurityManager sm = null; + + protected HashSet threadPool = new HashSet(); + + @BeforeClass + public static void init() { + try { + properties = new ConfigurationProvider().getJsap(); + } catch (SEPAPropertiesException | SEPASecurityException e) { + assertFalse("Configuration not found", false); + } + if (properties.isSecure()) { + int port = properties.getSubscribePort(); + if (port == -1) + url = "wss://" + properties.getDefaultHost() + properties.getSubscribePath(); + else + url = "wss://" + properties.getDefaultHost() + ":" + String.valueOf(port) + + properties.getSubscribePath(); + + try { + sm = new SEPASecurityManager(); + } catch (SEPASecurityException e) { + assertFalse("Security exception " + e.getMessage(), false); + } + } else { + int port = properties.getSubscribePort(); + if (port == -1) + url = "ws://" + properties.getDefaultHost() + properties.getSubscribePath(); + else + url = "ws://" + properties.getDefaultHost() + ":" + String.valueOf(port) + + properties.getSubscribePath(); + } + } + + @Test//(timeout = 60000) + public void Connect() throws URISyntaxException, SEPASecurityException, DeploymentException, IOException { + int n = 1000; + sync.reset(); + + for (int i = 0; i < n; i++) { + ClientManager client = ClientManager.createClient(); + if (properties.isSecure()) { + SslEngineConfigurator config = new SslEngineConfigurator(sm.getSSLContext()); + config.setHostVerificationEnabled(false); + client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, config); + } + client.connectToServer(new TyrusWebsocketClient(sync), new URI(url)); + + } + + sync.waitEvents(n); + } + +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java deleted file mode 100644 index a8295664..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketClient.java +++ /dev/null @@ -1,89 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; -import org.junit.BeforeClass; -import org.junit.Test; - -import it.unibo.arces.wot.sepa.ConfigurationProvider; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -import static org.junit.Assert.assertFalse; - -public class ITWebSocketClient { - protected final Logger logger = LogManager.getLogger(); - protected static JSAP properties = null; - protected static String url = null; - - class WebsocketTest extends WebSocketClient { - - public WebsocketTest(URI serverUri) { - super(serverUri); - } - - @Override - public void onOpen(ServerHandshake handshakedata) { - logger.debug("@onOpen: "+handshakedata.getHttpStatusMessage()); - } - - @Override - public void onMessage(String message) { - logger.debug("@onMessage: "+message); - } - - @Override - public void onClose(int code, String reason, boolean remote) { - logger.debug("@onClose code:"+code+" reason:"+reason+" remote:"+remote); - } - - @Override - public void onError(Exception ex) { - logger.error(ex.getMessage()); - } - - } - - @BeforeClass - public static void init() throws Exception { - properties = new ConfigurationProvider().getJsap(); - if(properties.isSecure()) { - int port = properties.getSubscribePort(); - if (port == -1) - url = "wss://"+properties.getDefaultHost()+properties.getSubscribePath(); - else - url = "wss://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); - } - else { - int port = properties.getSubscribePort(); - if (port == -1) - url = "ws://"+properties.getDefaultHost()+properties.getSubscribePath(); - else - url = "ws://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); - } - } - - @Test(timeout=20000) - public void Connect() throws URISyntaxException, InterruptedException, SEPASecurityException, IOException { - - - for (int i=0; i < 100; i++) { - WebsocketTest client = new WebsocketTest(new URI(url)); - - if (properties.isSecure()) { - SEPASecurityManager sm = new SEPASecurityManager(); - client.setSocket(sm.getSSLSocket()); - } - - assertFalse("Failed to connect",!client.connectBlocking()); - } - } - -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 46602ff1..b0d619d7 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -1,7 +1,7 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; +import java.io.IOException; import java.net.URISyntaxException; -import java.util.ArrayList; import java.util.concurrent.atomic.AtomicLong; import org.apache.logging.log4j.LogManager; @@ -17,8 +17,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -29,7 +27,9 @@ public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { protected final Logger logger = LogManager.getLogger(); private WebsocketSubscriptionProtocol client = null; + private static SEPASecurityManager sm = null; + private static AtomicLong subscribes = new AtomicLong(0); private String spuid = null; @@ -50,11 +50,14 @@ public void before() throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, URISyntaxException { if (provider.getJsap().isSecure()) { client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath(), sm, this); + provider.getJsap().getSubscribePath()); + client.enableSecurity(sm); } else client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath(), this); + provider.getJsap().getSubscribePath()); + client.setHandler(this); + subscribes.set(0); } @@ -63,13 +66,9 @@ public void after() { } - @Test(timeout = 5000) - public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 5000, sm); - - logger.debug(request); - - client.subscribe(request); + @Test (timeout = 5000) + public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, IOException { + client.subscribe(provider.buildSubscribeRequest("RANDOM", 5000, sm)); while (subscribes.get() != 1) { synchronized (subscribes) { @@ -86,21 +85,23 @@ public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, S assertFalse("Failed to subscribe", subscribes.get() != 1); } - @Test(timeout = 5000) - public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - int n = 50; - - SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 5000, sm); + @Test (timeout = 5000) + public void MultipleSubscribes() throws IOException { + int n = 95; for (int i = 0; i < n; i++) { - logger.debug(request); - client.subscribe(request); + try { + client.subscribe(provider.buildSubscribeRequest("RANDOM", 5000, sm)); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); + } } while (subscribes.get() < n) { synchronized (subscribes) { try { - subscribes.wait(); + subscribes.wait(5000); + logger.warn("Subscribes: "+subscribes.get()); } catch (InterruptedException e) { } @@ -112,24 +113,13 @@ public void SubscribexN() throws SEPAPropertiesException, SEPASecurityException, client.close(); } - @Test(timeout = 10000) - public void SubscribeMxN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { + @Test (timeout = 5000) + public void MultipleClientsAndMultipleSubscribes() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, IOException { int n = 10; int m = 5; - ArrayList clients = new ArrayList(); - - for (int i = 0; i < m; i++) { - if (provider.getJsap().isSecure()) { - clients.add(new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath(), sm, this)); - } else - clients.add(new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath(), this)); - } - for (int i = 0; i < m; i++) { - new Subscriber(clients.get(i),n).start(); + new Subscriber(n,this).start(); } while (subscribes.get() < n * m) { @@ -148,21 +138,23 @@ public void SubscribeMxN() throws SEPAPropertiesException, SEPASecurityException } class Subscriber extends Thread { - private WebsocketSubscriptionProtocol client; - private int n; + private final WebsocketSubscriptionProtocol client; + private final int n; - public Subscriber(WebsocketSubscriptionProtocol client, int n) { - this.client = client; + public Subscriber(int n,ISubscriptionHandler handler) throws SEPASecurityException { + client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), + provider.getJsap().getSubscribePath()); + + if (provider.getJsap().isSecure()) client.enableSecurity(sm); + client.setHandler(handler); + this.n = n; } public void run() { for (int j = 0; j < n; j++) { - SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 500, sm); - - logger.debug(request); try { - client.subscribe(request); + client.subscribe(provider.buildSubscribeRequest("RANDOM", 500, sm)); } catch (SEPAProtocolException e) { logger.error(e.getMessage()); } @@ -170,12 +162,11 @@ public void run() { } } - @Test(timeout = 5000) - public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException { - SubscribeRequest request = provider.buildSubscribeRequest("RANDOM", 500, sm); - + @Test (timeout = 5000) + public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, IOException { spuid = null; - client.subscribe(request); + + client.subscribe(provider.buildSubscribeRequest("RANDOM", 500, sm)); while (subscribes.get() != 1) { synchronized (subscribes) { @@ -189,9 +180,7 @@ public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecuri assertFalse("Failed to subscribe", subscribes.get() != 1); - UnsubscribeRequest unsub = provider.buildUnsubscribeRequest(spuid, 500, sm); - - client.unsubscribe(unsub); + client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 500, sm)); while (subscribes.get() != 0) { synchronized (subscribes) { diff --git a/client-api/src/test/resources/.gitignore b/client-api/src/test/resources/.gitignore new file mode 100644 index 00000000..7c8889b0 --- /dev/null +++ b/client-api/src/test/resources/.gitignore @@ -0,0 +1 @@ +/dashboard.properties diff --git a/client-api/src/test/resources/log4j2.xml b/client-api/src/test/resources/log4j2.xml index 639d3c4c..4deda79a 100644 --- a/client-api/src/test/resources/log4j2.xml +++ b/client-api/src/test/resources/log4j2.xml @@ -19,14 +19,14 @@ ALL Integer.MAX_VALUE - + - + diff --git a/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java b/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java index 8c81e311..0626a9cb 100644 --- a/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java +++ b/engine/src/main/java/it/arces/wot/sepa/engine/gates/Gate.java @@ -56,7 +56,7 @@ public final void onError(Exception e) { @Override public void notifyEvent(Notification notify) throws SEPAProtocolException { - logger.trace("@notifyEvent: " + notify); + logger.debug("@notifyEvent: " + notify); send(notify.toString()); } diff --git a/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java b/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java index 07c2ec41..46a16700 100644 --- a/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java +++ b/engine/src/main/java/it/arces/wot/sepa/engine/gates/WebsocketGate.java @@ -23,6 +23,7 @@ public WebsocketGate(WebSocket s,Scheduler scheduler){ public void send(String ret) throws SEPAProtocolException { try{ socket.send(ret); + logger.debug("Sent: "+ret); } catch(Exception e){ logger.warn("Socket: "+socket.hashCode()+" failed to send response: "+ret+" Exception:"+e.getMessage()); diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java index c86ba3a9..ba84d515 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/dependability/AuthorizationManager.java @@ -253,7 +253,7 @@ public static synchronized Response register(String identity) { //Check if entity is authorized to request credentials if (!authorizeIdentity(identity)) { - logger.error("Not authorized identity "+identity); + logger.warn("Not authorized identity "+identity); return new ErrorResponse(HttpStatus.SC_UNAUTHORIZED,"not_authorized_identity","Client "+identity+" is not authorized"); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java index 2e8dfa2c..e95775a9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java @@ -1,6 +1,5 @@ package it.unibo.arces.wot.sepa.engine.processing; -import org.apache.jena.atlas.lib.Sink; import org.apache.jena.graph.Triple; import org.apache.jena.query.Query; import org.apache.jena.query.QueryFactory; @@ -10,7 +9,6 @@ import org.apache.jena.sparql.core.BasicPattern; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.core.TriplePath; -import org.apache.jena.sparql.lang.UpdateParserFactory; import org.apache.jena.sparql.lang.arq.ParseException; import org.apache.jena.sparql.modify.request.*; import org.apache.jena.sparql.syntax.*; @@ -21,11 +19,8 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class SPARQLAnalyzer { diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java index 4ea85410..6a8165c9 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/subscriptions/SPUManager.java @@ -194,7 +194,7 @@ private Response internalUnsubscribe(String sid, String gid, boolean dep) { requests.remove(sub.getSPU().getSubscribe()); handlers.remove(spuid); - logger.debug("@unsubscribe active SPUs: " + spus.size()); + logger.info("@unsubscribe active SPUs: " + spus.size()); SPUManagerBeans.setActiveSPUs(spus.size()); } } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java index 38be9880..99a10f50 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/http/handler/RegisterHandler.java @@ -133,7 +133,7 @@ public void handle(HttpRequest data, HttpAsyncExchange exchange, HttpContext con if (cred.getClass().equals(ErrorResponse.class)) { ErrorResponse error = (ErrorResponse) cred; - logger.error(error.toString()); + logger.warn(error.toString()); HttpUtilities.sendFailureResponse(exchange, error); return; diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java index 9720cbbd..20e597a0 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/protocol/websocket/WebsocketServer.java @@ -74,15 +74,14 @@ public void onOpen(WebSocket conn, ClientHandshake handshake) { fragmentedMessages.put(conn, null); - logger.debug("@onOpen websocket: " + conn + " GID: " + gate.getGID() + " Total sockets: " - + gates.size()); + logger.debug("@onOpen (sockets: " + gates.size()+") GID: " + gate.getGID() + " socket: "+conn); } } @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { synchronized (gates) { - logger.trace("@onClose socket: " + conn + " reason: " + reason + " remote: "+remote); + logger.debug("@onClose socket: " + conn + " reason: " + reason + " remote: "+remote); fragmentedMessages.remove(conn); @@ -117,6 +116,9 @@ public void onMessage(WebSocket conn, String message) { synchronized (gates) { try { if (gates.get(conn) != null) gates.get(conn).onMessage(message); + else { + logger.error("Gate NOT FOUND: "+conn); + } } catch (SEPAProtocolException e) { logger.error(e); } diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java index 659c7af1..458b17e4 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/scheduling/Scheduler.java @@ -62,31 +62,36 @@ public Scheduler(EngineProperties properties) { setName("SEPA-Scheduler"); } - + public ScheduledRequest schedule(InternalRequest request, ResponseHandler handler) { if (request == null || handler == null) { logger.error("Request handler or request are null"); return null; } - // Add request to the scheduler queue (null means no more tokens) - ScheduledRequest scheduled = queue.addRequest(request, handler); + ScheduledRequest scheduled = null; + synchronized (responders) { + // Add request to the scheduler queue (null means no more tokens) + scheduled = queue.addRequest(request, handler); - // No more tokens - if (scheduled == null) { - SchedulerBeans.newRequest(request, false); - logger.error("Request refused: too many pending requests: " + request); - return null; - } + // No more tokens + if (scheduled == null) { + SchedulerBeans.newRequest(request, false); + logger.error("Request refused: too many pending requests: " + request); + return null; + } - logger.info(">> " + scheduled); + logger.info(">> " + scheduled); - Timings.log(request); + Timings.log(request); - SchedulerBeans.newRequest(request, true); + SchedulerBeans.newRequest(request, true); - // Register response handlers - responders.put(scheduled.getToken(), handler); + // Register response handlers + + logger.trace("Register handler: " + handler + " token: " + scheduled.getToken()); + responders.put(scheduled.getToken(), handler); + } return scheduled; } @@ -94,7 +99,7 @@ public ScheduledRequest schedule(InternalRequest request, ResponseHandler handle public void finish() { running.set(false); } - + @Override public void run() { while (running.get()) { @@ -107,29 +112,30 @@ public void run() { int token = response.getToken(); // Send response back - ResponseHandler handler = responders.get(token); - if (handler == null) { - logger.error("Response handler is null (token #" + token + ")"); - - } else { - try { - handler.sendResponse(response.getResponse()); - } catch (SEPAProtocolException e) { - logger.error("Failed to send response: " + e.getMessage()); + synchronized (responders) { + ResponseHandler handler = responders.get(token); + if (handler == null) { + logger.error("Response handler is null (token #" + token + ")"); + + } else { + logger.trace("Handler: " + handler + " response: " + response); + try { + handler.sendResponse(response.getResponse()); + } catch (SEPAProtocolException e) { + logger.error("Failed to send response: " + e.getMessage()); + } } + + // Remove handlers + responders.remove(token); } - // Remove handlers - responders.remove(token); - } catch (InterruptedException e) { running.set(false); } } } - - public String getStatistics() { return SchedulerBeans.getStatistics(); } diff --git a/engine/src/main/resources/log4j2.xml b/engine/src/main/resources/log4j2.xml index 639d3c4c..98daf0bd 100644 --- a/engine/src/main/resources/log4j2.xml +++ b/engine/src/main/resources/log4j2.xml @@ -19,14 +19,14 @@ ALL Integer.MAX_VALUE - + - + diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java index dc057360..7a3042ee 100644 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java +++ b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java @@ -1,14 +1,7 @@ package it.unibo.arces.wot.sepa.engine.processing; -import org.apache.jena.sparql.lang.arq.ParseException; -import org.apache.jena.update.Update; -import org.apache.jena.update.UpdateFactory; import org.junit.Test; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - import static org.junit.Assert.*; public class SPARQLAnalyzerTest { diff --git a/engine/src/test/resources/log4j2.xml b/engine/src/test/resources/log4j2.xml index ab9391a7..75cb9ac1 100644 --- a/engine/src/test/resources/log4j2.xml +++ b/engine/src/test/resources/log4j2.xml @@ -1,33 +1,22 @@ - + - + - + - - + + From efb0300afda95a03790476fdd814201c9e4a16cf Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 26 Sep 2018 14:48:18 +0200 Subject: [PATCH 60/76] Working on JUnit tests --- .../protocol/websocket/ITWebSocketSubscriptionProtocol.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index 28593e4e..fe7f8b2f 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -5,8 +5,6 @@ import java.net.URISyntaxException; import java.util.concurrent.atomic.AtomicLong; -import it.unibo.arces.wot.sepa.api.ITSPARQL11SEProtocol; -import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.After; @@ -20,8 +18,6 @@ import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.request.SubscribeRequest; -import it.unibo.arces.wot.sepa.commons.request.UnsubscribeRequest; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; From 20ccf9a2c46e2c26201a39672dd86eceb9fd828d Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 26 Sep 2018 14:51:29 +0200 Subject: [PATCH 61/76] Fixed problems after merge --- .../java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java | 2 +- .../api/protocol/websocket/ITWebSocketSubscriptionProtocol.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 931761a3..00c7ffcd 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -24,6 +24,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -233,7 +234,6 @@ public void Subscribe() } @Test (timeout = 30000) - @Test(timeout = 100000) public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { int n = 10; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index fe7f8b2f..d4e47024 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -41,7 +41,7 @@ public static void init() throws SEPAPropertiesException, SEPASecurityException, provider = new ConfigurationProvider(); if (provider.getJsap().isSecure()) { - ClassLoader classLoader = ITSPARQL11SEProtocol.class.getClassLoader(); + ClassLoader classLoader = ITWebSocketSubscriptionProtocol.class.getClassLoader(); File keyFile = new File(classLoader.getResource("sepa.jks").getFile()); sm = new SEPASecurityManager(keyFile.getPath(), "sepa2017", "sepa2017", provider.getJsap().getAuthenticationProperties()); From a12a7529b3456a23f69722ed0c24f0cfb7dbbed4 Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 26 Sep 2018 14:52:17 +0200 Subject: [PATCH 62/76] Remove SPARQLAnalyzer class. Missing reference to JENA library --- .../engine/processing/SPARQLAnalyzer.java | 260 ------------------ 1 file changed, 260 deletions(-) delete mode 100644 engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java deleted file mode 100644 index e95775a9..00000000 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzer.java +++ /dev/null @@ -1,260 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import org.apache.jena.graph.Triple; -import org.apache.jena.query.Query; -import org.apache.jena.query.QueryFactory; -import org.apache.jena.query.Syntax; -import org.apache.jena.sparql.algebra.*; -import org.apache.jena.sparql.algebra.op.OpBGP; -import org.apache.jena.sparql.core.BasicPattern; -import org.apache.jena.sparql.core.Quad; -import org.apache.jena.sparql.core.TriplePath; -import org.apache.jena.sparql.lang.arq.ParseException; -import org.apache.jena.sparql.modify.request.*; -import org.apache.jena.sparql.syntax.*; -import org.apache.jena.update.Update; -import org.apache.jena.update.UpdateFactory; -import org.apache.jena.update.UpdateRequest; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - - -public class SPARQLAnalyzer { - - String test = null; - - public void setString(String s) { - test = s; - } - - class MyTransform extends TransformCopy - { - @Override - public Op transform(OpBGP opBGP) - { - // create a new construct query - Query q = QueryFactory.make(); - q.setQueryConstructType(); - - // parse the bgp - BasicPattern b = opBGP.getPattern(); - Iterator opIterator = b.iterator(); - Template ttt = new Template(b); - q.setConstructTemplate(ttt); - ElementGroup body = new ElementGroup(); - ElementUnion union = new ElementUnion(); - - while (opIterator.hasNext()){ - Triple bb = opIterator.next(); - - // for the query - ElementTriplesBlock block = new ElementTriplesBlock(); // Make a BGP - block.addTriple(bb); - body.addElement(block); - logger.debug(bb.toString()); - - // union - union.addElement(block); - - } - - q.setQueryPattern(body); - q.setQueryPattern(union); - - setString(q.toString()); - logger.debug(q.toString()); - - return opBGP; - } - } - - class ToConstructUpdateVisitor extends UpdateVisitorBase{ - private UpdateConstruct result = new UpdateConstruct("",""); - @Override - public void visit(UpdateDataInsert updateDataInsert) { - Query insertQuery = createBaseConstruct(new QuadAcc(updateDataInsert.getQuads())); - String insertString = insertQuery.isUnknownType() ? "" : insertQuery.serialize() + "WHERE{}"; - result = new UpdateConstruct("",insertString); - } - - - - @Override - public void visit(UpdateDataDelete updateDataDelete) { - Query deleteQuery = createBaseConstruct(new QuadAcc(updateDataDelete.getQuads())); - String deleteString = deleteQuery.isUnknownType() ? "" : deleteQuery.serialize()+"WHERE{}"; - result = new UpdateConstruct(deleteString,""); - } - - @Override - public void visit(UpdateDeleteWhere updateDeleteWhere) { - Query updateDeleteQuery = createBaseConstruct(new QuadAcc(updateDeleteWhere.getQuads())); - if(!updateDeleteQuery.isUnknownType()) { - ElementGroup where = new ElementGroup(); - for (Quad q : updateDeleteWhere.getQuads()) { - where.addTriplePattern(q.asTriple()); - } - updateDeleteQuery.setQueryPattern(where); - result = new UpdateConstruct(updateDeleteQuery.serialize(), ""); - } - } - - @Override - public void visit(UpdateModify updateModify) { - String insertString = ""; - String deleteString = ""; - - if(updateModify.hasDeleteClause() && !updateModify.getDeleteAcc().getQuads().isEmpty()){ - Template constructDelete = new Template(updateModify.getDeleteAcc()); - Query constructQueryDelete = new Query(); - constructQueryDelete.setQueryConstructType(); - constructQueryDelete.setConstructTemplate(constructDelete); - constructQueryDelete.setQueryPattern(updateModify.getWherePattern()); - deleteString = constructQueryDelete.toString(); - } - - if(updateModify.hasInsertClause() && !updateModify.getInsertAcc().getQuads().isEmpty()){ - Template constructInsert = new Template(updateModify.getInsertAcc()); - Query constructQueryInsert = new Query(); - constructQueryInsert.setQueryConstructType(); - constructQueryInsert.setConstructTemplate(constructInsert); - constructQueryInsert.setQueryPattern(updateModify.getWherePattern()); - insertString = constructQueryInsert.serialize(); - } - - result = new UpdateConstruct(deleteString,insertString); - - } - - @Override - public void visit(UpdateClear update) { - String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct(deleteConstruct,""); - } - - @Override - public void visit(UpdateDrop update) { - String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct(deleteConstruct,""); - } - - @Override - public void visit(UpdateCopy update) { - String deleteConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getDest().getGraph().getURI()+"> { ?s ?p ?o } . }"; - String insertConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getSrc().getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct(deleteConstruct,insertConstruct); - } - - @Override - public void visit(UpdateAdd update) { - String insertConstruct = "CONSTRUCT { ?s ?p ?o } WHERE { GRAPH <"+update.getDest().getGraph().getURI()+"> { ?s ?p ?o } . }"; - result = new UpdateConstruct("",insertConstruct); - } - - //TODO: Move - - public UpdateConstruct getResult() { - return result; - } - - private Query createBaseConstruct( QuadAcc quads) { - Query result = new Query(); - if(!quads.getQuads().isEmpty()){ - Template construct = new Template(quads); - result = new Query(); - result.setQueryConstructType(); - result.setConstructTemplate(construct); - } - return result; - } - - - } - - // attributes - private String sparqlText; - private final static Logger logger = LogManager.getLogger("SPARQLAnalyzer"); - - // Constructor - SPARQLAnalyzer(String request){ - // store the query text - sparqlText = request; - } - - - UpdateConstruct getConstruct() { - UpdateRequest updates = UpdateFactory.create(sparqlText); - for(Update up : updates){ - ToConstructUpdateVisitor updateVisitor = new ToConstructUpdateVisitor(); - up.visit(updateVisitor); - return updateVisitor.getResult(); - } - throw new IllegalArgumentException("No valid operation found"); - } - - // LUTT generator - List getLutt(){ - - // debug print - logger.debug("Analyzing query " + sparqlText); - - // create a variable for the LUTT - List lutt = new ArrayList(); - - // extract basic graph patterns - Query q = QueryFactory.create(sparqlText); - Element e = q.getQueryPattern(); - - // This will walk through all parts of the query - ElementWalker.walk(e, - - // For each element... - new ElementVisitorBase() { - - // ...when it's a block of triples... - public void visit(ElementPathBlock el) { - - // ...go through all the triples... - Iterator triples = el.patternElts(); - while (triples.hasNext()) { - - // get the current triple pattern - TriplePath t = triples.next(); - lutt.add(t); - - // debug print - logger.debug("Found Triple Pattern: " + t.getSubject() + " " + t.getPredicate() + " " + t.getObject()); } - } - } - ); - - // return! - return lutt; - } - - // Construct Generator - String getConstructFromQuery() throws ParseException { - - // This method allows to derive the CONSTRUCT query - // from the SPARQL SUBSCRIPTION - - // get the algebra from the query - - Query qqq = QueryFactory.create(sparqlText, Syntax.syntaxSPARQL); - Op op = Algebra.compile(qqq); - - // get the algebra version of the construct query and - // convert it back to query - Transform transform = new MyTransform() ; - op = Transformer.transform(transform, op) ; - Query q = OpAsQuery.asQuery(op); - // return - return test; - - } - -} \ No newline at end of file From 875924c731031b7486d421eb7ffb3137e7f6073f Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 26 Sep 2018 14:53:02 +0200 Subject: [PATCH 63/76] Remove SPARQLAnalyzer JUnit --- .../engine/processing/SPARQLAnalyzerTest.java | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java diff --git a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java b/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java deleted file mode 100644 index 7a3042ee..00000000 --- a/engine/src/test/java/it/unibo/arces/wot/sepa/engine/processing/SPARQLAnalyzerTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package it.unibo.arces.wot.sepa.engine.processing; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class SPARQLAnalyzerTest { - private static final int DELETE_OUT = 1; - private static final int INSERT_OUT = 2; - private static final int INPUT = 0; - - private String [][] io = new String[][]{ - {"INSERT { }WHERE{}","","CONSTRUCT{.}WHERE{}"}, - {"DELETE WHERE{?a ?c}","CONSTRUCT{?a?c.}WHERE{?a?c}",""}, - {"CLEAR GRAPH ","CONSTRUCT{?s?p?o}WHERE{GRAPH{?s?p?o}.}",""}, - {"DELETE { }WHERE{?a ?b ?c}","CONSTRUCT{.}WHERE{?a?b?c}",""}, - {"DELETE { } INSERT { }WHERE{}","CONSTRUCT{.}WHERE{}","CONSTRUCT{.}WHERE{}"}, - {"DELETE { } INSERT { }WHERE{?a ?b ?c}","CONSTRUCT{.}WHERE{?a?b?c}","CONSTRUCT{.}WHERE{?a?b?c}"}, - {"INSERT DATA{ . }","","CONSTRUCT{..}WHERE{}"}, - {"DELETE DATA{ . }","CONSTRUCT{..}WHERE{}",""}, - }; - -//TODO Test move copy add operations - - @Test - public void setString() { - } - - @Test - public void getConstruct() { - for(String[] test : io){ - SPARQLAnalyzer sparqlAnalyzer = new SPARQLAnalyzer(test[INPUT]); - - UpdateConstruct construct = sparqlAnalyzer.getConstruct(); - assertEquals(test[DELETE_OUT], construct.getDeleteConstruct().replaceAll("\\s+","")); - - assertEquals(test[INSERT_OUT], construct.getInsertConstruct().replaceAll("\\s+","")); - } - } - - @Test - public void getLutt() { - } - - @Test - public void getConstructFromQuery() { - } -} \ No newline at end of file From 6845ff00ef9abf7e63688c0e3823bc03ac27d3fd Mon Sep 17 00:00:00 2001 From: Luca Roffia Date: Wed, 26 Sep 2018 17:04:15 +0200 Subject: [PATCH 64/76] Come on Travis! --- .../wot/sepa/api/SubscriptionProtocol.java | 4 +- .../unibo/arces/wot/sepa/ConnectTester.java | 61 ------ .../arces/wot/sepa/ConnectTesterTyrus.java | 51 ----- .../java/it/unibo/arces/wot/sepa/Sync.java | 14 +- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 147 ++++---------- .../arces/wot/sepa/{ => api}/Publisher.java | 41 ++-- .../arces/wot/sepa/{ => api}/Subscriber.java | 59 ++---- .../websocket/ITSEPAWebsocketClient.java | 90 --------- .../websocket/ITTyrusWebSocketClient.java | 10 +- .../ITWebSocketSubscriptionProtocol.java | 188 ++++-------------- .../api/protocol/websocket/Subscriber.java | 75 +++++++ .../websocket}/TyrusWebsocketClient.java | 6 +- .../security/ITSEPASecurityManager.java | 1 + 13 files changed, 207 insertions(+), 540 deletions(-) delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java rename client-api/src/test/java/it/unibo/arces/wot/sepa/{ => api}/Publisher.java (64%) rename client-api/src/test/java/it/unibo/arces/wot/sepa/{ => api}/Subscriber.java (68%) delete mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java create mode 100644 client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java rename client-api/src/test/java/it/unibo/arces/wot/sepa/{ => api/protocol/websocket}/TyrusWebsocketClient.java (77%) diff --git a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java index ea7988c5..a8afece5 100644 --- a/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java +++ b/client-api/src/main/java/it/unibo/arces/wot/sepa/api/SubscriptionProtocol.java @@ -1,7 +1,7 @@ package it.unibo.arces.wot.sepa.api; import java.io.Closeable; -import java.io.IOException; +//import java.io.IOException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; @@ -14,7 +14,7 @@ public interface SubscriptionProtocol extends Closeable { public void enableSecurity(SEPASecurityManager sm) throws SEPASecurityException; - public void close() throws IOException; +// public void close() throws IOException; public void subscribe(SubscribeRequest request) throws SEPAProtocolException; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java deleted file mode 100644 index bf0890b0..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTester.java +++ /dev/null @@ -1,61 +0,0 @@ -package it.unibo.arces.wot.sepa; - -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.java_websocket.client.WebSocketClient; -import org.java_websocket.handshake.ServerHandshake; - -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -public class ConnectTester extends WebSocketClient { - protected Logger logger = LogManager.getLogger(); - protected String url = null; - protected final Sync sync; - - public ConnectTester(URI server,Sync s,SEPASecurityManager sm) throws URISyntaxException, SEPASecurityException { - super(server); - sync = s; - - if (sm != null) setSocket(sm.getSSLSocket()); - } - - @Override - public void onOpen(ServerHandshake handshakedata) { - logger.debug("@onOpen: " + handshakedata.getHttpStatusMessage()); - - sync.event(); - } - - @Override - public void onMessage(String message) { - logger.debug("@onMessage: " + message); - } - - @Override - public void onClose(int code, String reason, boolean remote) { - logger.warn("@onClose code:" + code - + " reason:" + reason - + " remote:" + remote - + " STATE: "+this.getReadyState()); -// +" isConnecting: "+isConnecting() -// +" isClosed: "+this.isClosed() -// +" isClosing: "+this.isClosing() -// +" isOpen:" + this.isOpen() -// +" isFulshAndClose: "+this.isFlushAndClose() -// +" isReuseAddr: "+this.isReuseAddr()); - - if (remote && code == -1 && getReadyState().equals(READYSTATE.NOT_YET_CONNECTED)) close(); - } - - @Override - public void onError(Exception ex) { - logger.error("@onError "+ex.getMessage()); - - //reconnect(); - } - -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java deleted file mode 100644 index f40c8761..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConnectTesterTyrus.java +++ /dev/null @@ -1,51 +0,0 @@ -package it.unibo.arces.wot.sepa; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -import javax.websocket.ClientEndpoint; -import javax.websocket.CloseReason; -import javax.websocket.EndpointConfig; -import javax.websocket.OnClose; -import javax.websocket.OnError; -import javax.websocket.OnMessage; -import javax.websocket.OnOpen; -import javax.websocket.Session; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -@ClientEndpoint -public class ConnectTesterTyrus { - - protected Logger logger = LogManager.getLogger(); - - @OnMessage - public String onMessage(String message, Session session) { - BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in)); - try { - logger.info("Received ...." + message); - String userInput = bufferRead.readLine(); - return userInput; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @OnClose - public void onClose(Session session, CloseReason closeReason) { - logger.info(String.format("Session %s close because of %s", session.getId(), closeReason)); - } - - @OnOpen - public void onOpen(Session session, EndpointConfig arg1) { - logger.info("Connected ... " + session.getId()); - - } - - @OnError - public void onError(Session session, Throwable t) { - t.printStackTrace(); - } -} \ No newline at end of file diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java index ce314677..0426adeb 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/Sync.java @@ -13,23 +13,23 @@ public synchronized void reset() { spuid = null; } - public long getSubscribes() { + public synchronized long getSubscribes() { return subscribes; } - public long getEvents() { + public synchronized long getEvents() { return events; } - public long getUnsubscribes() { + public synchronized long getUnsubscribes() { return unsubscribes; } - public void resetSpuid() { + public synchronized void resetSpuid() { spuid = null; } - public String getSpuid() { + public synchronized String getSpuid() { return spuid; } @@ -73,8 +73,8 @@ public synchronized void unsubscribe() { notify(); } - public synchronized void subscribe(String s, String alias) { - spuid = s; + public synchronized void subscribe(String id, String alias) { + spuid = id; subscribes++; notify(); } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 00c7ffcd..6458a034 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -1,15 +1,12 @@ package it.unibo.arces.wot.sepa.api; import it.unibo.arces.wot.sepa.ConfigurationProvider; -import it.unibo.arces.wot.sepa.Publisher; -import it.unibo.arces.wot.sepa.Subscriber; import it.unibo.arces.wot.sepa.Sync; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.QueryResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.AuthenticationProperties; @@ -17,8 +14,6 @@ import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.pattern.JSAP; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -32,20 +27,18 @@ import static org.junit.Assert.*; public class ITSPARQL11SEProtocol { - protected final static Logger logger = LogManager.getLogger(); - - protected static JSAP properties = null; + private static JSAP properties = null; + private static ConfigurationProvider provider; + private static SEPASecurityManager sm; - private static SPARQL11Protocol client; - private final static String VALID_ID = "SEPATest"; private final static String NOT_VALID_ID = "RegisterMePlease"; private final static Sync sync = new Sync(); + + private static SPARQL11Protocol client; private final ArrayList subscribers = new ArrayList(); private final ArrayList publishers = new ArrayList(); - - private static ConfigurationProvider provider; @BeforeClass public static void init() throws Exception { @@ -70,7 +63,7 @@ public void beginTest() throws IOException, SEPAProtocolException, SEPAPropertie sync.reset(); - if (sm != null) + if (properties.isSecure()) client = new SPARQL11Protocol(sm); else client = new SPARQL11Protocol(); @@ -79,35 +72,21 @@ public void beginTest() throws IOException, SEPAProtocolException, SEPAPropertie publishers.clear(); Response ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); - - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); - } - - } assertFalse(String.valueOf(ret), ret.isError()); } @After public void endTest() throws IOException, InterruptedException { - if (client != null) - client.close(); + client.close(); - for (Subscriber sub : subscribers) - sub.close(); + for (Subscriber sub : subscribers) sub.close(); - for (Publisher pub : publishers) { - pub.finish(); - pub.interrupt(); - pub.join(); - } + for (Publisher pub : publishers) pub.close(); } @Test(timeout = 5000) public void RegisterNotAllowed() throws SEPASecurityException, SEPAPropertiesException { - if (sm != null) { + if (properties.isSecure()) { Response response = sm.register(NOT_VALID_ID); assertFalse("Failed to register a not valid ID", !response.isError()); } @@ -115,36 +94,20 @@ public void RegisterNotAllowed() throws SEPASecurityException, SEPAPropertiesExc @Test(timeout = 5000) public void Register() throws SEPASecurityException, SEPAPropertiesException { - if (sm != null) { + if (properties.isSecure()) { Response response = sm.register(VALID_ID); assertFalse("Failed to register a valid ID", response.isError()); } } @Test(timeout = 5000) - public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { + public void DeleteAllWithCheck() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { // Delete all triples Response ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); - - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - ret = client.update(provider.buildUpdateRequest("DELETE_ALL", 5000,sm)); - } - - } assertFalse(String.valueOf(ret), ret.isError()); // Evaluate if the store is empty ret = client.query(provider.buildQueryRequest("COUNT", 5000,sm)); - - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - ret = client.query(provider.buildQueryRequest("COUNT", 5000,sm)); - } - - } assertFalse(String.valueOf(ret), ret.isError()); QueryResponse results = (QueryResponse) ret; @@ -156,13 +119,13 @@ public void DeleteAll() throws SEPAPropertiesException, SEPASecurityException, I } } - @Test(timeout = 30000) + @Test(timeout = 15000) public void RequestToken() throws SEPASecurityException, SEPAPropertiesException, InterruptedException { - if (sm != null) { - for (int i = 0; i < 2000; i++) { + if (properties.isSecure()) { + for (int i = 0; i < 100; i++) { String authorization = sm.getAuthorizationHeader(); assertFalse("Failed to get authorization header", authorization == null); - Thread.sleep(10); + Thread.sleep(100); } } } @@ -170,24 +133,12 @@ public void RequestToken() throws SEPASecurityException, SEPAPropertiesException @Test(timeout = 1000) public void Update() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); - } - } assertFalse(String.valueOf(ret), ret.isError()); } @Test(timeout = 5000) public void Query() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.query(provider.buildQueryRequest("ALL", 5000,sm)); - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - client.query(provider.buildQueryRequest("ALL", 5000,sm)); - } - } assertFalse(String.valueOf(ret), ret.isError()); } @@ -195,31 +146,18 @@ public void Query() throws IOException, SEPAPropertiesException, SEPASecurityExc public void UpdateAndQuery() throws IOException, SEPAPropertiesException, SEPASecurityException, InterruptedException { Response ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); - - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - ret = client.update(provider.buildUpdateRequest("VAIMEE", 5000,sm)); - } - } assertFalse(String.valueOf(ret), ret.isError()); ret = client.query(provider.buildQueryRequest("VAIMEE", 5000,sm)); - if (ret.isError()) { - ErrorResponse error = (ErrorResponse) ret; - if (error.isTokenExpiredError()) { - client.query(provider.buildQueryRequest("VAIMEE", 5000,sm)); - } - } - assertFalse(String.valueOf(ret), ret.isError()); + assertFalse(String.valueOf(ret), ((QueryResponse) ret).getBindingsResults().size() != 1); } @Test(timeout = 5000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - subscribers.add(new Subscriber("ALL", sm, sync)); + subscribers.add(new Subscriber("ALL", sync)); for (Subscriber sub : subscribers) sub.start(); @@ -236,12 +174,12 @@ public void Subscribe() @Test (timeout = 30000) public void Subscribe3xN() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - int n = 10; + int n = 5; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("ALL", sm, sync)); - subscribers.add(new Subscriber("RANDOM", sm, sync)); - subscribers.add(new Subscriber("RANDOM1", sm, sync)); + subscribers.add(new Subscriber("ALL", sync)); + subscribers.add(new Subscriber("RANDOM", sync)); + subscribers.add(new Subscriber("RANDOM1", sync)); } for (Subscriber sub : subscribers) sub.start(); @@ -258,15 +196,14 @@ public void Subscribe3xN() @Test(timeout = 5000) public void Unsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, InterruptedException { - subscribers.add(new Subscriber("ALL", sm, sync)); - for (Subscriber sub : subscribers) - sub.start(); + + subscribers.add(new Subscriber("ALL", sync)); + for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); - - for (Subscriber sub : subscribers) sub.unsubscribe(sync.getSpuid()); - sync.waitEvents(subscribers.size()); + + for (Subscriber sub : subscribers) sub.unsubscribe(sync.getSpuid()); sync.waitUnsubscribes(subscribers.size()); assertFalse("Subscribes:" + sync.getSubscribes() + "(" + subscribers.size() + ")", @@ -281,12 +218,12 @@ public void Unsubscribe() public void Notify() throws IOException, IllegalArgumentException, SEPAProtocolException, SEPAPropertiesException, SEPASecurityException, InterruptedException { - subscribers.add(new Subscriber("VAIMEE", sm, sync)); + subscribers.add(new Subscriber("VAIMEE", sync)); for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); - publishers.add(new Publisher("VAIMEE", sm, 1)); + publishers.add(new Publisher("VAIMEE", 1)); for (Publisher pub : publishers) pub.start(); sync.waitEvents(1); @@ -303,14 +240,15 @@ public void NotifyNxN() throws IOException, IllegalArgumentException, SEPAProtoc int n = 5; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("RANDOM", sm, sync)); - publishers.add(new Publisher("RANDOM", sm, n)); + subscribers.add(new Subscriber("RANDOM", sync)); + publishers.add(new Publisher("RANDOM", n)); } for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); - + sync.waitEvents(subscribers.size()); + for (Publisher pub : publishers) pub.start(); sync.waitEvents(subscribers.size() + subscribers.size() * publishers.size() * publishers.size()); @@ -328,12 +266,14 @@ public void UpdateHeavyLoad() throws InterruptedException, SEPAPropertiesExcepti int n = 5; for (int i = 0; i < n; i++) { - publishers.add(new Publisher("RANDOM", sm, n)); - publishers.add(new Publisher("RANDOM1", sm, n)); - publishers.add(new Publisher("VAIMEE", sm, n)); + publishers.add(new Publisher("RANDOM", n)); + publishers.add(new Publisher("RANDOM1", n)); + publishers.add(new Publisher("VAIMEE", n)); } for (Publisher pub : publishers) pub.start(); + + // Wait all publishers to complete for (Publisher pub : publishers) pub.join(); } @@ -343,12 +283,12 @@ public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProt int n = 5; for (int i = 0; i < n; i++) { - subscribers.add(new Subscriber("ALL", sm, sync)); - subscribers.add(new Subscriber("RANDOM", sm, sync)); - subscribers.add(new Subscriber("RANDOM1", sm, sync)); + subscribers.add(new Subscriber("ALL", sync)); + subscribers.add(new Subscriber("RANDOM", sync)); + subscribers.add(new Subscriber("RANDOM1", sync)); - publishers.add(new Publisher("RANDOM", sm, n)); - publishers.add(new Publisher("RANDOM1", sm, n)); + publishers.add(new Publisher("RANDOM", n)); + publishers.add(new Publisher("RANDOM1", n)); } int events = 4 * n * n * n + subscribers.size(); @@ -356,6 +296,7 @@ public void Notify3Nx2N() throws IOException, IllegalArgumentException, SEPAProt for (Subscriber sub : subscribers) sub.start(); sync.waitSubscribes(subscribers.size()); + sync.waitEvents(subscribers.size()); for (Publisher pub : publishers) pub.start(); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java similarity index 64% rename from client-api/src/test/java/it/unibo/arces/wot/sepa/Publisher.java rename to client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java index 9437045b..124cda87 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -1,58 +1,53 @@ -package it.unibo.arces.wot.sepa; +package it.unibo.arces.wot.sepa.api; +import java.io.Closeable; import java.io.IOException; import java.util.concurrent.atomic.AtomicLong; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.protocol.SPARQL11Protocol; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Response; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import static org.junit.Assert.*; - -public class Publisher extends Thread { +class Publisher extends Thread implements Closeable { protected final Logger logger = LogManager.getLogger(); private final SEPASecurityManager sm; private final SPARQL11Protocol client; private final String id; - private AtomicLong running; + private final AtomicLong running; private static ConfigurationProvider provider; - public Publisher(String id,SEPASecurityManager sm,long n) throws SEPAPropertiesException, SEPASecurityException { - this.sm = sm; + public Publisher(String id,long n) throws SEPAPropertiesException, SEPASecurityException { + provider = new ConfigurationProvider(); + this.id = id; - if (sm != null) + if (provider.getJsap().isSecure()) { + sm = new SEPASecurityManager(); client = new SPARQL11Protocol(sm); - else + } + else { + sm = null; client = new SPARQL11Protocol(); + } running = new AtomicLong(n); - - provider = new ConfigurationProvider(); } public void run() { while(running.get() > 0) { Response ret = client.update(provider.buildUpdateRequest(id,5000,sm)); - -// if (ret.isError()) { -// ErrorResponse error = (ErrorResponse) ret; -// logger.error(error); -// if (error.isTokenExpiredError()) { -// client.update(provider.buildUpdateRequest(id,5000,sm)); -// } -// else -// assertFalse(error.toString(),true); -// } + if (ret.isError()) { + logger.error(ret); + } running.set(running.get()-1); } @@ -63,7 +58,7 @@ public void run() { } } - public void finish() { + public void close() { running.set(0); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java similarity index 68% rename from client-api/src/test/java/it/unibo/arces/wot/sepa/Subscriber.java rename to client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java index 4628b586..f056ea1d 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -1,11 +1,13 @@ -package it.unibo.arces.wot.sepa; +package it.unibo.arces.wot.sepa.api; +import java.io.Closeable; import java.io.IOException; -import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import it.unibo.arces.wot.sepa.ConfigurationProvider; +import it.unibo.arces.wot.sepa.Sync; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; import it.unibo.arces.wot.sepa.api.SPARQL11SEProtocol; import it.unibo.arces.wot.sepa.api.SubscriptionProtocol; @@ -17,34 +19,31 @@ import it.unibo.arces.wot.sepa.commons.response.Notification; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -public class Subscriber extends Thread implements ISubscriptionHandler { +class Subscriber extends Thread implements ISubscriptionHandler, Closeable { protected final Logger logger = LogManager.getLogger(); private final SEPASecurityManager sm; private final SPARQL11SEProtocol client; private final String id; private final Sync sync; - private String spuid; - - private AtomicBoolean subscribing = new AtomicBoolean(false); - private AtomicBoolean unsubscribing = new AtomicBoolean(false); private static ConfigurationProvider provider; - public Subscriber(String id, SEPASecurityManager sm, Sync sync) + public Subscriber(String id, Sync sync) throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException { provider = new ConfigurationProvider(); - - this.sm = sm; + this.id = id; this.sync = sync; SubscriptionProtocol protocol; - if (sm != null) { + if (provider.getJsap().isSecure()) { + sm = new SEPASecurityManager(); protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); protocol.enableSecurity(sm); } else { + sm = null; protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); } @@ -59,23 +58,25 @@ public void run() { subscribe(); wait(); } catch (SEPAProtocolException | InterruptedException e) { + try { + client.close(); + } catch (IOException e1) { + logger.error(e1.getMessage()); + } return; } } } - public void close() throws IOException { - client.close(); + public void close() { interrupt(); } private void subscribe() throws SEPAProtocolException, InterruptedException { - subscribing.set(true); client.subscribe(provider.buildSubscribeRequest(id, 5000, sm)); } public void unsubscribe(String spuid) throws SEPAProtocolException, InterruptedException { - unsubscribing.set(true); client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000, sm)); } @@ -95,39 +96,12 @@ public void onBrokenConnection() { @Override public void onError(ErrorResponse errorResponse) { logger.error("@onError: " + errorResponse); - if (errorResponse.isTokenExpiredError()) { - if (subscribing.get()) - try { - logger.warn("Token is expired. Renew token and subscribe again"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - client.subscribe(provider.buildSubscribeRequest(id, 5000, sm)); - } catch (SEPAProtocolException e) { - logger.error(e.getMessage()); - } - while (unsubscribing.get()) - try { - logger.warn("Token is expired. Renew token and unsubscribe again"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 5000, sm)); - } catch (SEPAProtocolException e) { - logger.error(e.getMessage()); - } - } } @Override public void onSubscribe(String spuid, String alias) { logger.debug("@onSubscribe: " + spuid + " alias: " + alias); - subscribing.set(false); if (sync != null) sync.subscribe(spuid, alias); } @@ -136,7 +110,6 @@ public void onSubscribe(String spuid, String alias) { public void onUnsubscribe(String spuid) { logger.debug("@onUnsubscribe: " + spuid); - unsubscribing.set(false); if (sync != null) sync.unsubscribe(); } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java deleted file mode 100644 index e1569818..00000000 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITSEPAWebsocketClient.java +++ /dev/null @@ -1,90 +0,0 @@ -package it.unibo.arces.wot.sepa.api.protocol.websocket; - -import static org.junit.Assert.assertFalse; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.junit.BeforeClass; -//import org.junit.Test; - -import it.unibo.arces.wot.sepa.ConfigurationProvider; -import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.protocols.websocket.JavaWebsocketClient; -import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; -import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; -import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; -import it.unibo.arces.wot.sepa.pattern.JSAP; - -public class ITSEPAWebsocketClient implements ISubscriptionHandler { - protected final Logger logger = LogManager.getLogger(); - protected static JSAP properties = null; - protected static String url = null; - - JavaWebsocketClient client = null; - - @BeforeClass - public static void init() throws Exception { - properties = new ConfigurationProvider().getJsap(); - if(properties.isSecure()) { - int port = properties.getSubscribePort(); - if (port == -1) - url = "wss://"+properties.getDefaultHost()+properties.getSubscribePath(); - else - url = "wss://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); - } - else { - int port = properties.getSubscribePort(); - if (port == -1) - url = "ws://"+properties.getDefaultHost()+properties.getSubscribePath(); - else - url = "ws://"+properties.getDefaultHost()+":"+String.valueOf(port)+properties.getSubscribePath(); - } - } - - //@Test(timeout = 10000) - public void Connect() throws InterruptedException, URISyntaxException, IOException, SEPASecurityException { - for (int i = 0; i < 100; i++) { - if(properties.isSecure()) { - SEPASecurityManager sm = new SEPASecurityManager(); - client = new JavaWebsocketClient(new URI(url), this, - sm.getSSLSocket()); - } - else { - client = new JavaWebsocketClient(new URI(url), this); - } - - assertFalse("Failed to connect", !client.connectBlocking()); - - } - } - - @Override - public void onSemanticEvent(Notification notify) { - logger.debug("@onSemanticEvent: " + notify); - } - - @Override - public void onBrokenConnection() { - logger.debug("@onBrokenConnection"); - } - - @Override - public void onError(ErrorResponse errorResponse) { - logger.debug("@onError: " + errorResponse); - } - - @Override - public void onSubscribe(String spuid, String alias) { - logger.debug("@onSubscribe: " + spuid + " alias: " + alias); - } - - @Override - public void onUnsubscribe(String spuid) { - logger.debug("@onUnsubscribe: " + spuid); - } -} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java index 552f2fd6..6fef9b36 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.util.HashSet; import javax.websocket.DeploymentException; @@ -19,7 +18,6 @@ import it.unibo.arces.wot.sepa.ConfigurationProvider; import it.unibo.arces.wot.sepa.Sync; -import it.unibo.arces.wot.sepa.TyrusWebsocketClient; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; @@ -36,8 +34,6 @@ public class ITTyrusWebSocketClient { protected static SEPASecurityManager sm = null; - protected HashSet threadPool = new HashSet(); - @BeforeClass public static void init() { try { @@ -68,9 +64,10 @@ public static void init() { } } - @Test//(timeout = 60000) + @Test (timeout = 5000) public void Connect() throws URISyntaxException, SEPASecurityException, DeploymentException, IOException { - int n = 1000; + int n = 100; + sync.reset(); for (int i = 0; i < n; i++) { @@ -81,7 +78,6 @@ public void Connect() throws URISyntaxException, SEPASecurityException, Deployme client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, config); } client.connectToServer(new TyrusWebsocketClient(sync), new URI(url)); - } sync.waitEvents(n); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index d4e47024..abc66005 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -1,9 +1,7 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.io.IOException; -import java.io.File; -import java.net.URISyntaxException; -import java.util.concurrent.atomic.AtomicLong; +import java.util.HashSet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -12,193 +10,87 @@ import org.junit.BeforeClass; import org.junit.Test; -import it.unibo.arces.wot.sepa.ConfigurationProvider; +import it.unibo.arces.wot.sepa.Sync; import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; -import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; import it.unibo.arces.wot.sepa.commons.response.ErrorResponse; import it.unibo.arces.wot.sepa.commons.response.Notification; -import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; - -import static org.junit.Assert.*; public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { protected final Logger logger = LogManager.getLogger(); - - private WebsocketSubscriptionProtocol client = null; - - private static SEPASecurityManager sm = null; - private static AtomicLong subscribes = new AtomicLong(0); + private static Sync sync = new Sync(); private String spuid = null; - - private static ConfigurationProvider provider; + + private HashSet subscribers = new HashSet(); @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { - provider = new ConfigurationProvider(); - - if (provider.getJsap().isSecure()) { - ClassLoader classLoader = ITWebSocketSubscriptionProtocol.class.getClassLoader(); - File keyFile = new File(classLoader.getResource("sepa.jks").getFile()); - sm = new SEPASecurityManager(keyFile.getPath(), "sepa2017", "sepa2017", - provider.getJsap().getAuthenticationProperties()); - sm.register("SEPATest"); - } + } @Before - public void before() - throws SEPAProtocolException, SEPASecurityException, SEPAPropertiesException, URISyntaxException { - if (provider.getJsap().isSecure()) { - client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath()); - client.enableSecurity(sm); - } else - client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath()); - - client.setHandler(this); - - subscribes.set(0); + public void before() { + sync.reset(); + subscribers.clear(); } @After - public void after() { - + public void after() throws IOException { + for (Subscriber s : subscribers) s.close(); } @Test (timeout = 5000) public void Subscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, IOException { - client.subscribe(provider.buildSubscribeRequest("RANDOM", 5000, sm)); + Subscriber s = new Subscriber(1,this); + subscribers.add(s); + s.start(); - while (subscribes.get() != 1) { - synchronized (subscribes) { - try { - subscribes.wait(); - } catch (InterruptedException e) { - - } - } - } - - client.close(); - - assertFalse("Failed to subscribe", subscribes.get() != 1); + sync.waitSubscribes(1); } @Test (timeout = 5000) - public void MultipleSubscribes() throws IOException { - int n = 95; + public void MultipleSubscribes() throws IOException, SEPASecurityException, SEPAPropertiesException { + int n = 10; for (int i = 0; i < n; i++) { - try { - client.subscribe(provider.buildSubscribeRequest("RANDOM", 5000, sm)); - } catch (SEPAProtocolException e) { - logger.error(e.getMessage()); - } - } - - while (subscribes.get() < n) { - synchronized (subscribes) { - try { - subscribes.wait(5000); - logger.warn("Subscribes: "+subscribes.get()); - } catch (InterruptedException e) { - - } - } + Subscriber s = new Subscriber(1,this); + subscribers.add(s); + s.start(); } - assertFalse("Failed to subscribe", subscribes.get() != n); - - client.close(); + sync.waitSubscribes(n); } @Test (timeout = 5000) public void MultipleClientsAndMultipleSubscribes() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, IOException { - int n = 10; + int n = 5; int m = 5; for (int i = 0; i < m; i++) { - new Subscriber(n,this).start(); + Subscriber s = new Subscriber(n,this); + subscribers.add(s); + s.start(); } - while (subscribes.get() < n * m) { - synchronized (subscribes) { - try { - subscribes.wait(); - } catch (InterruptedException e) { - - } - } - } - - assertFalse("Failed to subscribe", subscribes.get() != n * m); - - client.close(); - } - - class Subscriber extends Thread { - private final WebsocketSubscriptionProtocol client; - private final int n; - - public Subscriber(int n,ISubscriptionHandler handler) throws SEPASecurityException { - client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), - provider.getJsap().getSubscribePath()); - - if (provider.getJsap().isSecure()) client.enableSecurity(sm); - client.setHandler(handler); - - this.n = n; - } - - public void run() { - for (int j = 0; j < n; j++) { - try { - client.subscribe(provider.buildSubscribeRequest("RANDOM", 500, sm)); - } catch (SEPAProtocolException e) { - logger.error(e.getMessage()); - } - } - } + sync.waitSubscribes(n*m); } @Test (timeout = 5000) public void SubscribeAndUnsubscribe() throws SEPAPropertiesException, SEPASecurityException, SEPAProtocolException, IOException { spuid = null; - client.subscribe(provider.buildSubscribeRequest("RANDOM", 500, sm)); - - while (subscribes.get() != 1) { - synchronized (subscribes) { - try { - subscribes.wait(); - } catch (InterruptedException e) { - - } - } - } + Subscriber s = new Subscriber(1,this); + subscribers.add(s); + s.start(); - assertFalse("Failed to subscribe", subscribes.get() != 1); - - client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 500, sm)); - - while (subscribes.get() != 0) { - synchronized (subscribes) { - try { - subscribes.wait(); - } catch (InterruptedException e) { - - } - } - } - - assertFalse("Failed to unsubscribe", subscribes.get() != 0); + sync.waitSubscribes(1); + + s.unsubscribe(spuid); - client.close(); + sync.waitUnsubscribes(1); } @Override @@ -219,20 +111,14 @@ public void onError(ErrorResponse errorResponse) { @Override public void onSubscribe(String spuid, String alias) { logger.debug("@onSubscribe: " + spuid + " alias: " + alias); - - synchronized (subscribes) { - this.spuid = spuid; - subscribes.set(subscribes.get() + 1); - subscribes.notify(); - } + this.spuid = spuid; + sync.subscribe(spuid, alias); } @Override public void onUnsubscribe(String spuid) { logger.debug("@onUnsubscribe " + spuid); - synchronized (subscribes) { - subscribes.set(subscribes.get() - 1); - subscribes.notify(); - } + + sync.unsubscribe(); } } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java new file mode 100644 index 00000000..e125b564 --- /dev/null +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java @@ -0,0 +1,75 @@ +package it.unibo.arces.wot.sepa.api.protocol.websocket; + +import java.io.Closeable; +import java.io.IOException; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import it.unibo.arces.wot.sepa.ConfigurationProvider; +import it.unibo.arces.wot.sepa.api.ISubscriptionHandler; +import it.unibo.arces.wot.sepa.api.protocols.websocket.WebsocketSubscriptionProtocol; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAPropertiesException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPAProtocolException; +import it.unibo.arces.wot.sepa.commons.exceptions.SEPASecurityException; +import it.unibo.arces.wot.sepa.commons.security.SEPASecurityManager; + +class Subscriber extends Thread implements Closeable { + private final WebsocketSubscriptionProtocol client; + private int n; + + private static ConfigurationProvider provider; + + protected final Logger logger = LogManager.getLogger(); + protected SEPASecurityManager sm = null; + + public Subscriber(int n, ISubscriptionHandler handler) throws SEPASecurityException, SEPAPropertiesException { + provider = new ConfigurationProvider(); + + if (provider.getJsap().isSecure()) + sm = new SEPASecurityManager(); + + client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), + provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); + + if (provider.getJsap().isSecure()) + client.enableSecurity(sm); + + client.setHandler(handler); + + this.n = n; + } + + public void run() { + for (int j = 0; j < n; j++) { + try { + client.subscribe(provider.buildSubscribeRequest("RANDOM", 500, sm)); + } catch (SEPAProtocolException e) { + logger.error(e.getMessage()); + } + } + + try { + synchronized(this) { + wait(); + } + } catch (InterruptedException e1) { + + } + try { + client.close(); + } catch (IOException e) { + logger.error(e.getMessage()); + } + } + + public void unsubscribe(String spuid) throws SEPAProtocolException { + client.unsubscribe(provider.buildUnsubscribeRequest(spuid, 500, sm)); + } + + @Override + public void close() throws IOException { + n = 0; + interrupt(); + } +} diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/TyrusWebsocketClient.java similarity index 77% rename from client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java rename to client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/TyrusWebsocketClient.java index fd4606eb..3b12c6a4 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/TyrusWebsocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/TyrusWebsocketClient.java @@ -1,4 +1,4 @@ -package it.unibo.arces.wot.sepa; +package it.unibo.arces.wot.sepa.api.protocol.websocket; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; @@ -7,7 +7,9 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class TyrusWebsocketClient extends Endpoint { +import it.unibo.arces.wot.sepa.Sync; + +class TyrusWebsocketClient extends Endpoint { protected Logger logger = LogManager.getLogger(); protected final Sync sync; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java index 71a368bb..65484b94 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -26,6 +26,7 @@ public class ITSEPASecurityManager { @BeforeClass public static void init() throws SEPAPropertiesException, SEPASecurityException, InterruptedException { app = new ConfigurationProvider().getJsap(); + if (app.isSecure()){ ClassLoader classLoader = ITSPARQL11SEProtocol.class.getClassLoader(); File keyFile = new File(classLoader.getResource("sepa.jks").getFile()); From 1e4aedc4f76defa7a929b54d84b3a7821d8688c7 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 12:28:28 +0200 Subject: [PATCH 65/76] Fix sepatest-secure There were artifacts from previous merge --- .../src/test/resources/sepatest-secure.jsap | 76 ------------------- 1 file changed, 76 deletions(-) diff --git a/client-api/src/test/resources/sepatest-secure.jsap b/client-api/src/test/resources/sepatest-secure.jsap index 1204107d..2f0bfade 100644 --- a/client-api/src/test/resources/sepatest-secure.jsap +++ b/client-api/src/test/resources/sepatest-secure.jsap @@ -1,5 +1,4 @@ { -<<<<<<< HEAD "host": "localhost", "oauth": { "enable": true, @@ -73,79 +72,4 @@ "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" } } -======= - "host": "localhost", - "oauth": { - "enable": true, - "register": "https://localhost:8443/oauth/register", - "tokenRequest": "https://localhost:8443/oauth/token", - "client_id": "0IWFPpcBdkiZDqsvd2g/hjES9a3bvWhES7ieo/oH1nJx/lBURPHmu/uw9rSqs52M", - "client_secret": "kMEeuqq/tj8yILnL4u0rNIJAPcdkLTfx6yIt4wOoV1F3dWmZkG8NJUJKzyyMoiat", - "jwt": "xabtQWoH8RJJk1FyKJ78J8h8i2PcWmAugfJ4J6nMd+1jVSoiipV4Pcv8bH+8wJLJ2yRaVage8/TzdZJiz2jdRP8bhkuNzFhGx6N1/1mgmvfKihLheMmcU0pLj5uKOYWFb+TB98n1IpNO4G69lia2YoR15LScBzibBPpmKWF+XAr5TeDDHDZQK4N3VBS/e3tFL/yOhkfC9Mw45s3mz83oyeoFFePUX8MQIuqd3TIcjOhoPgYWd0E+L/EN5wItL5/n78pX/8mVZcpxdyNNqr3bVvrCi0I84mIAefwQ0GyPxFhUHu9PtVNQnXchZuFgppX/YDtOesZSxfIoffUpHFPBY3u4FRIYwpSZX96Knnp0J22RQm+0l8yobik3z6jftw0jbF5+/YC6PnfZT3Wzb6PRJPuVkDzpo+BTC9eKx87GEj8VjtfXjbYRTeZNumD+59wL5kV/OrntNqNQD+IzAYoIZk4rlRbNouNnvT0laEhV012tSD1uAfNUxAlZjSbSMTp5bPNp7PoutMr5q6zPYfAC1PTKnVdkD1CDNqZnhB838WDeISfVzXsf7dsZ0+SkNPtx2kMUYCOYsxNJxyzza3lmaCuvxfnDT3g5F41/p/zX1tXYy6emVfdOWSkJNm1z0FJB/ZIUES0WAA5UEM3kejND++vvIQr38ar72HdFzRvP2V29CsaE5PMRRRZIE5ru9Zwgdb5lfMdwDi4sZkQdNRGHiOfRCT9D92mFVps6s6kv7HKojx05R9WKMDG8bEmSgMYSYQlQzLm93Ardw/hpDoB1/DfNRxbc/GVNZEVOoRVMye8/vICZtxvVeKmu4QawWKSBtrXelWUT8AHTG6v/c88pZjtJWDzy6YIIXLDQ2eJPu30mt3gLfS2ukIV4Tl5Oqu3T1IIghmNgek8vwWNeuG/JGeKrfUp6X6mMH9hdmj5+naOIr8V5rUKCjXnlWLAsrGdOvV8vuYYbx2IFQScZQJD/sTKj3gs6yeYpOwQ2iEs9asA=", - "expires": "9SN2d0sZEIN16Kts7LxUuQ==", - "type": "XPrHEX2xHy+5IuXHPHigMw==" - }, - "sparql11protocol": { - "protocol": "https", - "port": 8443, - "query": { - "path": "/secure/query", - "method": "POST", - "format": "JSON" - }, - "update": { - "path": "/secure/update", - "method": "POST", - "format": "JSON" - } - }, - "sparql11seprotocol": { - "protocol": "wss", - "availableProtocols": { - "ws": { - "port": 9000, - "path": "/subscribe" - }, - "wss": { - "port": 9443, - "path": "/secure/subscribe" - } - } - }, - "namespaces": { - "sepa": "http://wot.arces.unibo.it/sepa#", - "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#" - }, - "updates": { - "DELETE_ALL": { - "sparql": "DELETE {GRAPH {?x ?y ?z} } WHERE {GRAPH {?x ?y ?z} }" - }, - "VAIMEE": { - "sparql": "DELETE {GRAPH {sepa:SV sepa:PV ?o}} WHERE {GRAPH {sepa:SV sepa:PV ?o}} ; INSERT DATA {GRAPH {sepa:SV sepa:PV \"ვაიმეე\"}}" - }, - "RANDOM": { - "sparql": "DELETE {GRAPH {sepa:S sepa:P ?o}} WHERE {GRAPH {sepa:S sepa:P ?o}} ; INSERT {GRAPH {sepa:S sepa:P ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" - }, - "RANDOM1": { - "sparql": "DELETE {GRAPH {sepa:S1 sepa:P1 ?o}} WHERE {graph {sepa:S1 sepa:P1 ?o}} ; INSERT {GRAPH {sepa:S1 sepa:P1 ?random}} WHERE {BIND(IRI(CONCAT(\"http://wot.arces.unibo.it/sepa#Random-\",STRUUID())) AS ?random)}" - } - }, - "queries": { - "VAIMEE": { - "sparql": "SELECT * WHERE {GRAPH {?x ?y \"ვაიმეე\"}}" - }, - "ALL": { - "sparql": "SELECT * WHERE {GRAPH {?x ?y ?z}}" - }, - "RANDOM": { - "sparql": "SELECT * WHERE {GRAPH {sepa:S sepa:P ?random}}" - }, - "RANDOM1": { - "sparql": "SELECT * WHERE {GRAPH {sepa:S1 sepa:P1 ?random}}" - }, - "COUNT": { - "sparql": "SELECT (COUNT(?x) AS ?n) WHERE {GRAPH {?x ?y ?z}}" - } - } ->>>>>>> branch 'UltraHighPerformance' of https://github.com/arces-wot/sepa.git } \ No newline at end of file From b2d4d151de4f94196fdda75088d3936445fc9da7 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 12:30:41 +0200 Subject: [PATCH 66/76] Fix test configuration environment Security manager was not correctly initialized because the SEPA keys file wasn't loaded from its resource path. --- .../unibo/arces/wot/sepa/ConfigurationProvider.java | 11 +++++++++++ .../java/it/unibo/arces/wot/sepa/api/Publisher.java | 2 +- .../java/it/unibo/arces/wot/sepa/api/Subscriber.java | 2 +- .../protocol/websocket/ITTyrusWebSocketClient.java | 6 ++++-- .../wot/sepa/api/protocol/websocket/Subscriber.java | 2 +- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java index 22562324..fb6fb3bd 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java @@ -61,6 +61,7 @@ public UpdateRequest buildUpdateRequest(String id, long timeout,SEPASecurityMana if (sm != null) try { authorization = sm.getAuthorizationHeader(); + logger.debug("Authorized"); } catch (SEPASecurityException | SEPAPropertiesException e) { logger.error(e.getMessage()); } @@ -111,6 +112,16 @@ public UnsubscribeRequest buildUnsubscribeRequest(String spuid, long timeout,SEP return new UnsubscribeRequest(spuid, authorization, timeout); } + + public SEPASecurityManager buildSecurityManager() throws SEPASecurityException { + String path = getClass().getClassLoader().getResource("sepa.jks").getPath(); + File f = new File(path); + if (!f.exists()) { + logger.error("File not found: " + path); + throw new SEPASecurityException("File not found: "+path); + } + return new SEPASecurityManager(f.getPath(), "sepa2017", "sepa2017",appProfile.getAuthenticationProperties()); + } public JSAP getJsap() { return appProfile; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java index 124cda87..02ec45b2 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Publisher.java @@ -31,7 +31,7 @@ public Publisher(String id,long n) throws SEPAPropertiesException, SEPASecurityE this.id = id; if (provider.getJsap().isSecure()) { - sm = new SEPASecurityManager(); + sm = provider.buildSecurityManager(); client = new SPARQL11Protocol(sm); } else { diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java index f056ea1d..a9e0ddb2 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/Subscriber.java @@ -38,7 +38,7 @@ public Subscriber(String id, Sync sync) SubscriptionProtocol protocol; if (provider.getJsap().isSecure()) { - sm = new SEPASecurityManager(); + sm = provider.buildSecurityManager(); protocol = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); protocol.enableSecurity(sm); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java index 6fef9b36..643bb62f 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java @@ -36,8 +36,10 @@ public class ITTyrusWebSocketClient { @BeforeClass public static void init() { + ConfigurationProvider provider = null; try { - properties = new ConfigurationProvider().getJsap(); + provider = new ConfigurationProvider(); + properties = provider.getJsap(); } catch (SEPAPropertiesException | SEPASecurityException e) { assertFalse("Configuration not found", false); } @@ -50,7 +52,7 @@ public static void init() { + properties.getSubscribePath(); try { - sm = new SEPASecurityManager(); + sm = provider.buildSecurityManager(); } catch (SEPASecurityException e) { assertFalse("Security exception " + e.getMessage(), false); } diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java index e125b564..d967c61e 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/Subscriber.java @@ -27,7 +27,7 @@ public Subscriber(int n, ISubscriptionHandler handler) throws SEPASecurityExcept provider = new ConfigurationProvider(); if (provider.getJsap().isSecure()) - sm = new SEPASecurityManager(); + sm = provider.buildSecurityManager(); client = new WebsocketSubscriptionProtocol(provider.getJsap().getDefaultHost(), provider.getJsap().getSubscribePort(), provider.getJsap().getSubscribePath()); From f71f3677350b07b57c1c5a6eed8bf6709f0d1464 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 12:32:14 +0200 Subject: [PATCH 67/76] Add Luca's email in travis notification (surprise) --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index de97ae20..4aff1aff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,10 @@ cache: - "$HOME/.m2" services: - docker +notifications: + email: + - relu.cri@gmail.com + - luca.roffia@unibo.it jobs: include: - stage: test From cba560d23b9f00842f1cbc9f3a71916214d01ab8 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 19:42:20 +0200 Subject: [PATCH 68/76] Add logging information about sepa configuration in stage Integration --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4aff1aff..b476655f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,11 +19,15 @@ jobs: - mvn package - cd engine/target - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar + - echo launching engine with this configuration + - cat engine.jpar - java -jar engine-0-SNAPSHOT.jar -engine=engine.jpar > log.txt & - sleep 30 - cd ../.. script: - mvn verify + after_failure: + - "cat engine/target/log.txt" - stage: integrationSecure before_script: - docker run --name blazegraph -d -p 9999:9999 nawer/blazegraph:2.1.1 From df657845667110df54ec93afe38c89c0b1e24edf Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 19:53:18 +0200 Subject: [PATCH 69/76] Modify engine defaults add scheduler timeout --- engine/defaults/engine.jpar | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/defaults/engine.jpar b/engine/defaults/engine.jpar index 9a36b9e4..bd85b5ce 100644 --- a/engine/defaults/engine.jpar +++ b/engine/defaults/engine.jpar @@ -1,7 +1,8 @@ { "parameters": { "scheduler": { - "queueSize": 100 + "queueSize": 100, + "timeout": 3000 }, "processor": { "updateTimeout": 5000, From c2e1110914444a54df6877a3ee5d50b620c76475 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 20:33:11 +0200 Subject: [PATCH 70/76] Add engine secure configuration --- engine/defaults/engine-secure.jpar | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 engine/defaults/engine-secure.jpar diff --git a/engine/defaults/engine-secure.jpar b/engine/defaults/engine-secure.jpar new file mode 100644 index 00000000..d74b958b --- /dev/null +++ b/engine/defaults/engine-secure.jpar @@ -0,0 +1,33 @@ +{ + "parameters": { + "scheduler": { + "queueSize": 100, + "timeout": 3000 + }, + "processor": { + "updateTimeout": 5000, + "queryTimeout": 5000, + "maxConcurrentRequests": 5 + }, + "spu": { + "timeout": 2000 + }, + "gates": { + "secure": true, + "ports": { + "http": 8000, + "ws": 9000, + "https": 8443, + "wss": 9443 + }, + "paths": { + "update": "/update", + "query": "/query", + "subscribe": "/subscribe", + "register": "/oauth/register", + "tokenRequest": "/oauth/token", + "securePath": "/secure" + } + } + } +} From aee48a134fe1a8e4d3ddc85d1a96d599b6d453d0 Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 20:45:44 +0200 Subject: [PATCH 71/76] Add logging for secure stage --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index b476655f..f0fbe37e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,10 +37,13 @@ jobs: - cd engine/target - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar - java -jar engine-0-SNAPSHOT.jar -engine=engine-secure.jpar > log.txt & + - cat engine.jpar - sleep 30 - cd ../.. script: - mvn verify -Dsecure=true + after_failure: + - "cat engine/target/log.txt" - stage: integration-virtuoso before_script: - docker run --name my-virtuoso -p 8890:8890 -p 1111:1111 -e DBA_PASSWORD=myDbaPassword -e SPARQL_UPDATE=true -e DEFAULT_GRAPH=http://www.example.com/my-graph -d tenforce/virtuoso From 43e98a22ce621c12ece489594fdae8645a63cbce Mon Sep 17 00:00:00 2001 From: reluc Date: Thu, 27 Sep 2018 21:01:42 +0200 Subject: [PATCH 72/76] Fix secure logging --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f0fbe37e..89481f6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,7 @@ jobs: - cd engine/target - mv endpoints/endpoint-blazegraph.jpar endpoint.jpar - java -jar engine-0-SNAPSHOT.jar -engine=engine-secure.jpar > log.txt & - - cat engine.jpar + - cat engine-secure.jpar - sleep 30 - cd ../.. script: From 44737496cbaf9e0e27592a748a7ff8d4d063322b Mon Sep 17 00:00:00 2001 From: reluc Date: Fri, 28 Sep 2018 17:38:34 +0200 Subject: [PATCH 73/76] Modify add some time to connect test --- .../wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java index 643bb62f..dda6f6b2 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java @@ -66,7 +66,7 @@ public static void init() { } } - @Test (timeout = 5000) + @Test (timeout = 10000) public void Connect() throws URISyntaxException, SEPASecurityException, DeploymentException, IOException { int n = 100; From 6e67c5c9f533b387f0ac6395d8448feceb42cd3e Mon Sep 17 00:00:00 2001 From: reluc Date: Fri, 28 Sep 2018 18:06:35 +0200 Subject: [PATCH 74/76] Fix virtuoso configuration file --- .travis.yml | 2 +- engine/endpoints/endpoint-virtuoso.jpar | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89481f6c..c5012956 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,7 +52,7 @@ jobs: - mvn package - cd engine/target - mv endpoints/endpoint-virtuoso.jpar endpoint.jpar - - java -jar engine-0-SNAPSHOT.jar > log.txt & + - java -jar engine-0-SNAPSHOT.jar -engine=engine.jpar > log.txt & - sleep 30 - cd ../.. script: diff --git a/engine/endpoints/endpoint-virtuoso.jpar b/engine/endpoints/endpoint-virtuoso.jpar index 3a54bbcc..37dce5b8 100644 --- a/engine/endpoints/endpoint-virtuoso.jpar +++ b/engine/endpoints/endpoint-virtuoso.jpar @@ -26,7 +26,7 @@ }, "update": { "path": "/sparql", - "method": "GET", + "method": "POST", "format": "JSON" } } From db0b7aa1753aee96bb5570f6daa49b2776edd15d Mon Sep 17 00:00:00 2001 From: reluc Date: Sat, 29 Sep 2018 11:11:01 +0200 Subject: [PATCH 75/76] Fix logger initialization logger file name property must be initialized on the engine class loading. Previously other classes were loading the logger before the file name was set, giving exceptions about the bad format of the file name. --- .../java/it/unibo/arces/wot/sepa/engine/core/Engine.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java index b6db35f5..fc8685f8 100644 --- a/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java +++ b/engine/src/main/java/it/unibo/arces/wot/sepa/engine/core/Engine.java @@ -85,7 +85,7 @@ public class Engine implements EngineMBean { private String endpointJpar = "endpoint.jpar"; // Logging file name - private void setLoggingFileName() { + static { // Logging TimeZone tz = TimeZone.getTimeZone("UTC"); DateFormat df = new SimpleDateFormat("yyyyMMdd_HH_mm_ss"); // Quoted "Z" to indicate UTC, no timezone offset @@ -190,8 +190,7 @@ public Engine(String[] args) { System.out .println("##########################################################################################"); - // Set logging file name with the current timestamp YYYYMMDD_HH_MM_SS - setLoggingFileName(); + // Command arguments parsingArgument(args); From 6e2e690176bf666fbbebe2a91d587013ad080036 Mon Sep 17 00:00:00 2001 From: reluc Date: Sat, 29 Sep 2018 11:37:23 +0200 Subject: [PATCH 76/76] Fix logger initialization in maven tests logger file name property must be initialized on test class loading. Notice that junit doesn't have the privileges to create logs folder, so now it's manually created --- .gitignore | 3 ++ .../arces/wot/sepa/ConfigurationProvider.java | 29 ++++++++++++++++++- .../wot/sepa/api/ITSPARQL11SEProtocol.java | 8 +++++ .../websocket/ITTyrusWebSocketClient.java | 8 +++++ .../ITWebSocketSubscriptionProtocol.java | 9 ++++++ .../security/ITSEPASecurityManager.java | 8 +++++ 6 files changed, 64 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2cf444de..a71e445e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,9 @@ engine/sepa.jks # timing created by engine engine/logs +# timing created by maven tests +logs + # Created by https://www.gitignore.io/api/java,maven,eclipse,intellij ### Eclipse ### diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java index fb6fb3bd..1924f509 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/ConfigurationProvider.java @@ -10,17 +10,44 @@ import it.unibo.arces.wot.sepa.pattern.JSAP; import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.Set; +import java.util.TimeZone; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class ConfigurationProvider { + static { + configureLogger(); + } + protected final Logger logger = LogManager.getLogger(); private final JSAP appProfile; private String prefixes = ""; - + + public static void configureLogger() { + // Logging + TimeZone tz = TimeZone.getTimeZone("UTC"); + DateFormat df = new SimpleDateFormat("yyyyMMdd_HH_mm_ss"); // Quoted "Z" to indicate UTC, no timezone offset + df.setTimeZone(tz); + String nowAsISO = df.format(new Date()); + System.setProperty("logFilename", nowAsISO); + + //Create file + final File logfolder = new File("logs/"); + if(!logfolder.exists()){ + logfolder.mkdir(); + } + + org.apache.logging.log4j.core.LoggerContext ctx = (org.apache.logging.log4j.core.LoggerContext) LogManager + .getContext(false); + ctx.reconfigure(); + } + public ConfigurationProvider() throws SEPAPropertiesException, SEPASecurityException { String jsapFileName = "sepatest.jsap"; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java index 6458a034..84be3b50 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/ITSPARQL11SEProtocol.java @@ -14,6 +14,7 @@ import it.unibo.arces.wot.sepa.commons.sparql.Bindings; import it.unibo.arces.wot.sepa.pattern.JSAP; +import org.apache.logging.log4j.LogManager; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -22,11 +23,18 @@ import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.TimeZone; import static org.junit.Assert.*; public class ITSPARQL11SEProtocol { + static { + ConfigurationProvider.configureLogger(); + } private static JSAP properties = null; private static ConfigurationProvider provider; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java index dda6f6b2..3c6d8868 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITTyrusWebSocketClient.java @@ -3,6 +3,10 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; import javax.websocket.DeploymentException; @@ -26,6 +30,10 @@ import static org.junit.Assert.assertFalse; public class ITTyrusWebSocketClient { + static { + ConfigurationProvider.configureLogger(); + } + protected final Logger logger = LogManager.getLogger(); protected static JSAP properties = null; diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java index abc66005..67ddb3ae 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/api/protocol/websocket/ITWebSocketSubscriptionProtocol.java @@ -1,8 +1,13 @@ package it.unibo.arces.wot.sepa.api.protocol.websocket; import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.HashSet; +import java.util.TimeZone; +import it.unibo.arces.wot.sepa.ConfigurationProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.After; @@ -19,6 +24,10 @@ import it.unibo.arces.wot.sepa.commons.response.Notification; public class ITWebSocketSubscriptionProtocol implements ISubscriptionHandler { + static { + ConfigurationProvider.configureLogger(); + } + protected final Logger logger = LogManager.getLogger(); private static Sync sync = new Sync(); diff --git a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java index 65484b94..8f2d3a12 100644 --- a/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java +++ b/client-api/src/test/java/it/unibo/arces/wot/sepa/commons/security/ITSEPASecurityManager.java @@ -1,6 +1,7 @@ package it.unibo.arces.wot.sepa.commons.security; import it.unibo.arces.wot.sepa.api.ITSPARQL11SEProtocol; +import org.apache.logging.log4j.LogManager; import org.junit.BeforeClass; import org.junit.Test; @@ -13,10 +14,17 @@ import it.unibo.arces.wot.sepa.pattern.JSAP; import java.io.File; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; import static org.junit.Assert.*; public class ITSEPASecurityManager { + static { + ConfigurationProvider.configureLogger(); + } private static SEPASecurityManager sm = null; private static JSAP app = null;