Title

Home

dydra web client support

This library implements a JavaScript web client interface to the Dydra RDF service. It mediates persistence for native JavaScript application and u/i data models to a remote Dydra service via GSP/HTTP and/or websockets. The remote service implements either W3C GSP-conformant transactional repositories, revisioned repositories which maintain data through PUT/POST/DELETE operations or CRDT-repositories which interpret PATCH opertations to replicate and reconcile data among multiple clients.

The library integrates an application data model with a remote store by reconciling representations in three realms:

  • web application data
    represented as JavaScript objects
    identified by a combination of instance identifier and application key values
    described by JavaScript object properties, each of which binds either an atomic value, an object, or an array of such
    combined into an active model which is associated with a revision
    JavaScript proxy objects monitor access to instance propertiess and propagate them to the store

  • intermediate graphs
    each a collection of triples
    each captures the object property changes associated with a given application model transaction
    a JSON-LD context governs the relation between terms and property values
    each associated with a revision to enable undo/redo

  • persistent data
    represented as a revisioned collection of RDF quads stored in a service repository
    where the subject iri correspond to the application model instance identifier
    the predicate arity is either one, for atomic values or higher for arrays
    the object terms are either literals or subject iri of related resources

The library concerns the application data, the intermediate graph and communicating it to the remote RDF repository. It is realized by three components in the client:

  • the GraphObject API
  • JavaScript / RDF mediation
  • the background remote request processing
        application API                         background requests  
    <-    GraphObject   ->               <- GraphStore + RDF GSP,SPARQL ->

    Object  -  field-cache  -  Graph  -  GSP/websockets  -  RDF-repository

                      JavaScript / RDF mediation  
              <---- Graph-Data-Model + RDFDatabase ----->

Application API

The application API supports the user interface data model. Its implmentation comprises the files

graph-object.js

It provides the abstract model class, GraphObject, which encapsulates intances of specializations to track field modifications and record a delta map for use by the store mediation layer. That relies on, in addition to the tracking logic, default methods to transform the delta map into an abstract patch according to instance state (clean, deleted, modified, new, etc.) and on logic to manage declarations for managed properties

GraphObject.prototype.asPatch[]()
GraphObject.prototype.managedProperties()
GraphObject.prototype.persistentProperties()
GraphObject.prototype.transactionalProperties()

Should the application require other than the generic transformations, it can override them as needed. In connection with these, the respective concrete U/I classes must They must also define respective static fields for

_persistentProperties
_transactionalProperties

to indicate which fields are to be managed. These are coallesced for the respective concrete class, upon first use.

JavaScript / RDF Mediation

The mediation layer implements the relation between the native JSON data model and its representation as RDF. Its elements incorporate two layers: absctract graph operations and RDF-specific implementations:

graph-database.js
RDF-database.js
graph-environment.js
RDF-environment.js

and includes the utility

uuid-v1.js

At the mediation layer, the class GraphDatabase provides the means for the application to realize persistence either by JDO-like reachability or through explicit store write operations. Either approach follows the pattern defined by IndexedDB (google, w3c), in that a GDBTransaction provides the context for both implicitly persistent model data manipulation and explicit store operations.

In order to propagate commits and accept remote notifications this relies on RDFGraph instances and it, in turn, on RDFEnvironment instances, to perform un/marshalling between RDF graphs and JSON objects.

The mediation layer provides the operations

GraphDatabase.objectStore
GraphDatabase.transaction
GDBObjectStore.put
GDBObjectStore.get
GDBObjectStore.delete
GDBObjectStore.attach
GDBObjectStore.detach
GDBTransaction.abort
GDBTransaction.commit
GDBTransaction.objectStore

which implement the abstract mechanisms to bind instance to stores, create transactions to govern changes and transform state changes onto store requests upon transaction completion.

They rely on the RDF-specific implementation to implement RDF instance identifiers and IndexedDB key functions to govern the relation between data model entities and their remote RDF representation.

The arguments to attach operations serve as roots to a reachability graph, of which the objects are registered with the transaction's active ObjectStore. When a transaction commits, this collection is examined and its field delta maps are interpreted to generate GSP PATCH operations to propagate to the remote replication service. If a transaction aborts, then the maps are used to roll the object states back view the method

GraphObject.prototype.rollback

Background Replication

The interaction with a remote RDF service for the actual data exchange is accomplished by a background via Promises. When either a transaction is committed explicitly or a chain of GDBObjectStore put operations completes, the deferred phase transforms the abstract patch descriptions into a concrete GSP request and executes it on the remote service.

This is defined in

rdf-database.js

as concrete implemetations for the operators

RDFDatabase.prototype.describe()
RDFDatabase.prototype.head()
RDFDatabase.prototype.get()
RDFDatabase.prototype.patch()

which rely on the RDFEnvironment implementation for

RDFEnvironment.prototype.createPatch()
RDFEnvironment.prototype.createNamedNode()

and GSP/SPARQL implementation for

GSP.get()
GSP.head()
GSP.patch()

The operations yielf IndexedDB Request results and completion notification is through the onsuccess property.

Background change notification

The application main thread receives changes from background websocket listeners, which accept patch requests from the remote service. These are translated back into property delta maps, which reference attached GraphObject instances and are passed to the application in the events

GraphObject.prototype.oncreate
GraphObject.prototype.onupdate
GraphObject.prototype.ondelete

for those instances to examine and apply with the methods

GraphObject.prototype.rollforward

graph-database.js

The classes

  • GraphDatabase
  • GDBTransaction
  • GDBObjectStore

provide the javascript/graph mediation layer in a form which combines the IndexedDB and JDO APIs. It support the standard operations

  • open, close
  • transaction
  • createObjectStore
  • get, put, delete

Additional operators extend IndexedDB semantics to accommodate basic JDO/JPA behaviour:

  • attach, detach
  • commit

The application-thread API operators transform between native javascript objects and graphs to be exchanged as websockets/fetch requests with a remote graph store which acts as the Graph storage service.
The object<->graph transformation relies on the GraphObject state tracking and the field<->term mapping mechanisms which a GDBObjectStore delegates to a GraphEnvironment.

The IndexedDB transaction behaviour is combined with that of JDO. The former recommendation specifies that, once no further operation is possible, a transaction commits.

http://blog.nparashuram.com/2011/11/indexeddb-apis-javascriptnext.html
https://w3c.github.io/IndexedDB/#async-execute-request

As per the w3c IndexedDB description, operations are queued as requests and each is run asynchronously, but in turn, in the order created. Upon completion, to notify the application either onerror or onsuccess is invoked for each request.
Once a notification returns and no further request is pending, it is expected that the active transaction have dynamic extent only, that is, the main thread maintains no control flow which expect the transaction to have indefinite extent and to be able to an additional request. Under this assumption, the transaction can be be committed as soon a no request is pending.
A non-local control transfer from a request notification should abort the transaction.
When the transaction commits, any managed changes which happen during the transaction's extent are marshalled and excuted as well, after which instance specific onsuccess/failure invoked. In addition to the implicit completion, an explicit commit operation can apply pending request and managed changes manually.

The put/get functions rely on promises to implement asynchronous behaviour. see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise For a put operation, the execution function marshals the object state into a pending request and immediately resolves the promise. It provides a then function which implements the IndexedDB asynchronous behaviour by iterating over all accumulated put requests, caching the request data to be transmitted when the transaction completes and invoking each request's onsuccess. For a get request, the execute function initiates the retrieval and resolves the promise when the response arrives, while the then function unmarshals the result and invokes the request's onsuccess to the the data to the application. When executing the then functions, shuld no request remain, the transaction is completed. but transmitting all accumulated combined put data and patches from managed objects.

The default implementation uses the W3C SPARQL and Graph Store Protocols to communicate with a CRDT-as-RDF service. A GraphEnvironment combines JSON-LD term/field mapping together with graph manipulation utilites from rdflib to provie the default implemention of the abstract interface.

View Source graph-database.js, line 3

graph-object.js

The class GraphObject is an abstract class which wraps each concrete instance in a proxy to mediate property access and implement a jdo/jpa-like state machine to control instance. The logic distinguished detached/attached situations wrt a ReplicaObjectStore and, for attached objects, constrcts for them a delta map and uses that cache upon tranaction completion to propagate changes to the respective remote storage service.

The state graph is a reduced version of the JDO space which corresponds to that model's 'persistent' states, and the 'transient' realm of that JDO space does not figure in this implementation.

That is, the reduced space includes just new clean dirty deleted with which the attachment/transaction status combines to yield the JDO equivalents.

see: https://db.apache.org/jdo/state_transition.html, https://en.wikipedia.org/wiki/Java_Persistence_API

For specialized GraphObject classes :

  • writes to managed properties side-effect the state.
  • attached objects also generate delta maps during a transaction.
  • transactional reads from managed properties require a valid state.

View Source graph-object.js, line 3

rdf-client.js

rdf-database.js

rdf-environment.js