This document describes the JSON SPARQL Application Profile (JSAP). JSAP is used to describe an application based on the SPARQL Event Processing Architecture (SEPA) and following the application design pattern named PAC (Producer-Aggregator-Consumer) described in this document. JSAP uses JSON as default format [[!RFC7159]] .
This is a first draft.

Introduction

This document first presents the concept of SPARQL Application Profile from a developer point a view. Developers are RECOMMENDED to follow the PAC design pattern presented in this document (i.e., an application is composed by a set of producers, consumers and aggregators). The role of JSAP is to collect the SPARQL updates [[sparql11-update]], SPARQL subscribes and the protocol parameters used by a SEPA application. JSAP MAY contain a list of SPARQL prefixes. In such a way, JSAP acts as an identity card of a SEPA application.

A JSAP file MUST be a JSON document compliant with RFC 7159 [[!RFC7159]] and SHOULD NOT be modified at runtime, as it describes the application scope. The need of runtime modifications of the JSAP file is to be considered as a bad practice. Other serialization formats (e.g., YAML, XML) MAY be used but their definition is out of the context of this document.

Document conventions

When this document uses the words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and RECOMMENDED, and the words appear as emphasized text, they must be interpreted as described in RFC 2119 [[!RFC2119]].

Terminology

JSAP
JSON SPARQL Application Profile (as defined by this document)
SPARQL
SPARQL Protocol and RDF Query Language (as defined by [[sparql11-overview]])
SPARQL 1.1 Update Language
As defined by [[sparql11-update]]
SPARQL 1.1 Query Language
As defined by [[sparql11-query]]
SPARQL 1.1 Subscribe Language
The subscription language introduced by the SEPA
SPARQL 1.1 SE Protocol
The protocol implemented by a SEPA broker
SEPA
SPARQL Event Processing Architecture
SEPA application
a collection of producers, consumers and aggregators (as defined by this document)
SEPA broker
The server component of the SEPA. It implements the publish-subscribe mechanisms and algorithms. Clients interact with a SEPA broker with the SPARQL 1.1 SE Protocol

Producer-Aggregator-Consumer design pattern

The following figure gives an overview of the RECOMMENDED application design pattern to be followed by SEPA applications developers.

PAC pattern

Fig. 1 - The SEPA application design pattern also know as "PAC-Pattern" (Producer-Aggregator-Consumer)

The design pattern assumes that a client SHOULD interact with a SEPA broker using two primitives: update and subscribe. The protocol used by a client to interact with a SEPA broker is described in SPARQL 1.1 Secure Event Protocol. While an update corresponds to a SPARQL 1.1 Update [[sparql11-update]], a subscribe is described in SPARQL 1.1 Subscribe Language. A subscribe acts as a persistent SPARQL 1.1 Query [[sparql11-query]] and the content of a notification contains the delta of the results since the previous notification (i.e., when a subscribe is invoked the current query results are returned to the client) as described in SPARQL 1.1 Subscribe Language. A client assumes a different role depending on how it interacts with a SEPA broker.

A producer invokes a SPARQL 1.1 Update [[sparql11-update]] and such update MUST be always the same (i.e., see forced bindings). A sensor producing some measures is an example.

A consumer invokes a SPARQL 1.1 Subscribe. Like the update of a producer, a consumer MUST invoke one and only one subscribe. When a consumer subscribes, it MAY replace some variables within the SPARQL 1.1 Subscribe with actual values (i.e., see forced bindings). An actuator subscribed to events (or commands) is an example.

An aggregator acts as follows: when it receives a notification, it processes the notification content and invokes an update (if needed). The processing of an aggregator can be combinatorial or sequential. In the former case there is no need of an internal context memory. To be classified as aggregator, a relation between the notification and the corresponding update SHOULD exist (i.e., an aggregator is not the sum of a producer and a consumer). In the PAC pattern, aggregators implement the application business logic.

The PAC pattern MAY be attractive for Web of Things applications: producers and consumers interact with the physical world and SHOULD be kept as simpler as possible. Aggregators MAY be more resources greedy (e.g., MAY run on a high performance server machine). This distinction is also very important for reuse and modularization: the same set of producers and consumers can be re-used in other applications and the application business logic can be changed (or extended) just by adding (or extending) aggregators.

JSAP structure

A JSAP is structured as follows.

{
  "host":"...",
  "oauth":{},
  "sparql11protocol":{},
  "sparql11seprotocol":{},
  "namespaces":{},
  "extended":{},
  "graphs":{
    "default-graph-uri": [],
    "named-graph-uri": [],
    "using-graph-uri": [],
    "using-named-graph-uri": []
  },
  "updates":{
    "UPDATE_IDENTIFIER_1":{},
    "...":{},
    "UPDATE_IDENTIFIER_N":{}
  },
  "queries":{
    "QUERY_IDENTIFIER_1":{},
    "...":{},
    "QUERY_IDENTIFIER_N":{}
  }
}
		
The host , sparql11protocol and sparql11seprotocol MUST be all present. The host member contains the host name or IP of the SEPA broker. The other two members allow to configure respectively the SPARQL 1.1 protocol [[sparql11-protocol]] and SPARQL 1.1 Secure Event Protocol. With reference to the SPARQL 1.1 protocol [[sparql11-protocol]], the graphs member MAY be used to specify zero or more default graph URIs ( default-graph-uri ) and named graph URIs ( named-graph-uri ) for queries (i.e., subscribes), as well as zero or more default graph URIs ( using-graph-uri ) and named graph URIs ( using-named-graph-uri ) for udpdates. The values of all these members MAY be overwritten within the updates and queries as described later. The extended member is optional and it MAY be used to store application specific data.

Protocol parameters

A semantic application profile, in order to fully describe an application, MUST contain the protocol parameters needed by an application to interact with a SEPA broker instance through the SPARQL 1.1 SE Protocol, like the HTTPS interface (used by secure SPARQL updates and SPARQL queries) and the Websocket interface (needed by (secure) SPARQL subscriptions). In JSAP, such parameters are described by two mandatory JSON objects named sparql11protocol and sparql11seprotocol .

{
  "sparql11protocol":{
    "protocol":"http",
    "port":8000,
    "query":{
      "path":"/query",
      "method":"POST",
      "format":"JSON"
    },
    "update":{
      "path":"/update",
      "method":"POST",
      "format":"JSON"
    }
  }
}

The value of the host member specifies the host where the SEPA broker is running (e.g., vaimee.org).

The port member specifies the port where the SEPA broker is listening for SPARQL 1.1 primitives (updates and queries).

The protocol member specifies the protocol used and it can assume the values: http or https .

The update and query members contain the following members: path (i.e., the path part of the URL), method (i.e., the HTTP method used) and format (i.e., the format of the response). According to the SPARQL 1.1 protocol [[sparql11-protocol]], for queries the format can be POST, GET, URL_ENCODED_POST, while for updates it can be POST or URL_ENCODED_POST. All the SEPA implementations MUST support JSON as return format, while other formats like XML, CSV or HTML MAY be supported.

{
  "sparql11seprotocol":{
    "protocol":"ws",
    "availableProtocols":{
      "ws":{
        "port":9000,
        "path":"/subscribe"
      },
      "wss":{
        "port":9443,
        "path":"/secure/subscribe"
      }
    }
  }
}
 
The URLs corresponding to the above protocol configuration (not secure) follow:

- Query: http://vaimee.org:8000/query

- Update: http://vaimee.org:8000/update

- Subscribe: ws://vaimee.org:9000/subscribe

Security

Connecting to a secure SEPA broker require the following changes to the above protocol configurations:
{
  "oauth":{
    "enable":true,
    "register":"https://localhost:8443/oauth/register",
    "tokenRequest":"https://localhost:8443/oauth/token"
  }
}  
The oauth member allows to specify the Authorization Server URLs used to register a new client identity and request tokens.
{
  "sparql11protocol":{
    "protocol":"https",
    "port":8443,
    "query":{
      "path":"/secure/query",
      "method":"POST",
      "format":"JSON"
    },
    "update":{
      "path":"/secure/update",
      "method":"POST",
      "format":"JSON"
    }
  }
}
{
  "sparql11seprotocol":{
    "protocol":"wss"
  }
}
The corresponding URLs would be as follows:

- SECURE Query: https://vaimee.org:8443/secure/query

- SECURE Update: https://vaimee.org:8443/secure/update

- SECURE Subscribe: wss://vaimee.org:9443/secure/subscribe

- Registration: https://vaimee.org:8443/oauth/register

- Token request: https://vaimee.org:8443/oauth/token

JSAP MAY also be used by clients to store credentials (i.e., clientId and clientSecret) and JSON Web Token. For security reasons, it is RECOMMENDED to encrypt those elements (e.g., using Advanced Encryption Standard (AES)) like in the following example:

{
  "oauth":{
    "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=="
  }
}

Namespaces

JSAP MAY include a set of namespaces used by SPARQL updates and queries. Client-side APIs SHOULD take the namespaces and prepend them to a SPARQL update/subscribe. This allows to simplify the SPARQL text by using qualified names for URIs [[xml-names]]. Namespaces are specified as a JSON object assigned to the key namespaces . In this object, every key represents a prefix, while the value is the relative namespace.

{
  "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#",
    "schema":"http://schema.org"
  }
}

Updates, queries and forced bindings

The main benefit of JSAP consists in the ability of creating templates for applications. Those templates act as an identity card of an application. JSAP MAY include two JSON objects named updates and queries listing all the SPARQL 1.1 updates [[sparql11-update]] and SPARQL 1.1 subscribes used by the application.The updates and queries members, if present, contain a list of objects sharing the structure here reported:

{
  "QUERY_OR_UPDATE_IDENTIFIER":{
    "sparql":"SPARQL Update or Query",
    "forcedBindings":{},
    "sparql11protocol":{},
    "sparql11seprotocol":{},
    "graphs":{}
  }
}

The value of the mandatory sparql member is the SPARQL query [[sparql11-query]] or update [[sparql11-update]]. The sparql11protocol , sparql11seprotocol and graphs members MAY be used to overwrite some protocol parameters.

SPARQL updates and subscribes can be fetched by the application and modified at run-time to fit the application needs. For example, a producer that updates the value of a temperature sensor will only need to fill a field in the template (i.e., the current value). Here is where the definition of forced bindings comes in help. A forced binding enables the developer to substitute at run-time a variable in a template with a custom value. To define forced bindings, the key forcedBindings MUST be used. The value is a JSON object. The variable of a forced binding is a key in that JSON object. Its value is again a JSON object containing the keys type and value . type is mandatory and MUST be one of "uri" , "bnode" , "literal" . The value key is optional and SHOULD be used to specify a default value for that variable.

{
  "forcedBindings":{
    "variable-X-uri":{
      "type":"uri",
      "value":"..."
    },
    "variable-Y-literal":{
      "type":"literal",
      "value":"...",
      "datatype":"XSD Datatype"
    },
    "variable-Z-bnode":{
      "type":"bnode",
      "value":"..."
    }
  }
}

The role of the forcedBindings member is to define which variables will be replaced at run-time by their actual values. The use of forced bindings can be appreciated by considering the following example. The updates listed within the updates member share a common template but with different forced bindings.

{
  "namespaces":{
    "rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#",
    "wot":"http://wot.arces.unibo.it/wot#"
  },
  "updates":{
    "UPDATE_ALL_TO_100":{
      "sparql":"DELETE {?sensor wot:hasValue ?oldValue} INSERT {?sensor wot:hasValue '100'} WHERE {?sensor rdf:type wot:SENSOR . ?sensor wot:hasValue ?oldValue}"
    },
    "UPDATE_ALL_TO_X":{
      "sparql":"DELETE {?sensor wot:hasValue ?oldValue} INSERT {?sensor wot:hasValue ?x} WHERE {?sensor rdf:type wot:SENSOR . ?sensor wot:hasValue ?oldValue}",
      "forcedBindings":{
        "x":{
          "type":"literal",
          "datatype":"xsd:integer"
        }
      }
    },
    "UPDATE_SENSOR_TO_X":{
      "sparql":"DELETE {?sensor wot:hasValue ?oldValue} INSERT {?sensor wot:hasValue ?x} WHERE {?sensor rdf:type wot:SENSOR . ?sensor wot:hasValue ?oldValue}",
      "forcedBindings":{
        "x":{
          "type":"literal",
          "datatype":"xsd:integer"
        },
        "sensor":{
          "type":"uri"
        }
      }
    }
  }
}

The UPDATE_ALL_TO_100 do not have any forced binding. The update will be issued as it is to the SEPA broker and the effect will be to update all the sensor data values (e.g., wot:hasValue) to "100" .

In the update "UPDATE_ALL_TO_X", the fixed value "100" has been replaced by the variable ?x , that is also a literal forced binding. At run-time, the a SEPA agent would replace the variable ?x with a literal value, like for example "46". The actual update issued to the SEPA broker follows and the effect will be to update all the sensor data values (e.g., wot:hasValue) to "46" .

The update "UPDATE_SENSOR_TO_X", is the same as the previous update, but the ?sensor variable is to be considered a forced binding. A SEPA agent is supposed to replace both the forced bindings (i.e, ?x and ?sensor ) before sending the update to the SEPA broker (e.g., ?x = "69" and ?sensor = wot:SENSOR_XYZ_URI ). The actual update issued to the SEPA broker follows and the effect will be to update the data value (e.g., wot:hasValue) of the sensor identified by the URI wot:SENSOR_XYZ_URI to "69" .

Eventually, from the application point of view, JSAP hides SPARQL complexity and allow the use of a simple portable interface to create Dynamic Linked Data applications.

A minimum working example: a SEPA based chat

In order to provide the reader with a better understanding of the SEPA application design pattern using the proposed JASP, this section presents a simple and at the same time meaningful example.

The example is a chat application where users exchange messages. A user should be able to send a message to an other user and be notified when the message has been received. In the following is described how this behaviour can be implemented thanks to the publish-subscribe mechanism provided by SEPA. According to the producer-aggregator-consumer design pattern proposed in this document, a chat client can be implemented by the following agents:

These entities can be mapped to the following interface of functions: These information define the semantic application and can be written in a first draft of the JSAP:
{
  "updates":{
    "SEND":{
      "sparql":"...",
      "forcedBindings":{
        "text":{
          "type":"literal"
        },
        "sender":{
          "type":"uri"
        },
        "receiver":{
          "type":"uri"
        }
      }
    },
    "SET_RECEIVED":{
      "sparql":"...",
      "forcedBindings":{
        "message":{
          "type":"uri"
        }
      }
    },
    "REMOVE":{
      "sparql":"...",
      "forcedBindings":{
        "message":{
          "type":"uri"
        }
      }
    }
  },
  "queries":{
    "SENT":{},
    "RECEIVED":{}
  }
}

After defining the message format (i.e. how it is encoded in RDF triples) the structure of the chat application obtained by this design step is depicted in the following figure.

chat

Fig. 2 - The SEPA Chat example: each client is composed by three SEPA agents (sender, receiver and remover)

Finally the primitives are implemented with SPARQL queries/updates and the JASP is filled with this information:

{
  "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"
        },
        "sender":{
          "type":"uri"
        },
        "receiver":{
          "type":"uri"
        }
      }
    },
    "SET_RECEIVED":{
      "sparql":"INSERT {?message schema:dateReceived ?time} WHERE {?message rdf:type schema:Message BIND(STR(now()) AS ?time)}",
      "forcedBindings":{
        "message":{
          "type":"uri"
        }
      }
    },
    "REMOVE":{
      "sparql":"DELETE {?message ?p ?o} WHERE {?message rdf:type schema:Message ; schema:dateReceived ?time . ?message ?p ?o}",
      "forcedBindings":{
        "message":{
          "type":"uri"
        },
        "time":{
          "type":"literal"
        }
      }
    }
  },
  "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"
        }
      }
    },
    "RECEIVED":{
      "sparql":"SELECT ?message ?time WHERE {?message schema:sender ?sender ; schema:dateReceived ?time ; rdf:type schema:Message}",
      "forcedBindings":{
        "sender":{
          "type":"uri"
        }
      }
    }
  }
}
          
At runtime when the two clients join the chat, their Receiver and Remover agents subscribe to the SEPA broker by replacing respectively the receiver forcedbinding in the SENT query and the sender forcedbinding in the RECEIVED query with the client identifier (e.g., schema:PersonURI1 for client #1 and schema:PersonURI2 for client #2). When client #1 sends a new message to client #2, it invokes the SEND update, first replacing the sender and receiver forced bindings with the current one (i.e., respectively schema:PersonURI1 and schema:PersonURI2) and the text binding with the message to be sent. The effect of the update is to create the bold graph shown in Fig. 2. This update triggers a notification for client #2: its Receiver agent inserts the dotted part of the graph to specify the receiving time. This is done by invoking the SET_RECEIVED update, replacing the message forced binding with the corresponding one included in the notification. This update triggers a notification on client #1: client #1 knows at this time that client #2 has received the message(i.e.,the clients are synchronized)and can delete the corresponding graph from the RDF store. This is done invoking the REMOVE update, replacing the message forced binding with the effective551 URI of the message to be removed. The effect of this update is two folds: the RDF store is cleaned by all messages that have been received (i.e., the store contains only the messages that have been sent but not yet received) and the Receiver agent is notified of the removed bindings (i.e., client #2 is aware that client #1 has been notified by the SET_RECEIVED update).

The flow of messages exchanged by the clients in the previous example is described by following UML sequence diagram:

SEPA chat

Fig. 3 - Send message; UML sequence diagram

Finally the following snippet show the full JSAP of the chat application with the configuration parameters

{
  "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}"
    }
  }
}

Acknowledgements

Editors would like to thanks the Advanced Research Center on Electronic Systems "Ercole De Castro" (ARCES) and the Computer Science and Engineering Department (DISI) of the University of Bologna, the European Commission and all the partners of the ARTEMIS projects who inspired the SPARQL Event Processing Architecture (SEPA).