Apache Jackrabbit : Frm HttpOperations

HTTP Operations

This document provides a description of the HTTP implementation of the operations exposed by the Oak Remote API.

Authentication

This section relies on the HTTP/1.1 Authentication framework defined by RFC7235. The idea is that an Oak server may define different authentication schemes, while the client may choose one of them according to his particular needs.

Authentication information is mandatory for every method described below, unless differently specified.

Basic

The basic authentication uses the authentication scheme described in Section 2 of RFC2617. The client provides a user name and password that will be authenticated by the repository using the normal login procedure.

Impersonation

The impersonation authentication uses an authentication scheme of Impersonate. The client must include one or more tokens that represents the principals of the target subject to impersonate. In example, an impersonation request sent by a client may contain the following header:

Authorization: Impersonate principals="bob,authors,reviewers"

to force the current request to be evaluated as the current subject is correctly logged in the repository and is an aggregation of the principals "bob", "authors" and "reviewers".

If this authentication method is chosen, the server will skip authentication completely. The server will assume that another part of the application is responsible for authentication, and will limit itself to evaluate the request according to the privileges granted to the impersonated user.

This authentication method is particularly risky, and it is meant to be used only if there is a high degree of confidentiality between the server and the client. In example, a server may want to provide this kind of authentication scheme as a challenge to a client only in case of a secure, encrypted connection or only if the client is part of a whitelist.

Read a revision

GET /revisions/last

In case of success, the server returns a 200 response with a content type of application/json. The body of the respnose is a JSON object, where the revision is the value of the revision property.

{
  "revision": "..."
}

The value of the revision property is an opaque value whose format and semantics are implementation dependent.

Read a tree

GET /revisions/:revision/tree/:path
GET /revisions/last/tree/:path

The :revision part of the URL is the revision that represents the state of the repository used when reading. A value for this parameter is obtained by using a dedicated method of this API. If the second kind of URL is used, where :revision assumes the value of last, the read operation is performed on the latest state of the repository known to the server.

The :path part of the URL is the path of the node in the repository that will be used as a root for the read operation.

Request parameters:

  • depth - the maximum depth of the tree returned by the response. This parameter is optional and is a single-value parameter.
  • properties - a glob representing a property filter for the returned tree. This parameter is optional and is a multi-value parameter. Multiple instances of this parameter can be included to specify different property filters.
  • children - a glob representing a filter for the child nodes included in the returned tree. This parameter is optional and is a multi-value parameter. Multiple instances of this parameter can be included to specify different child node filters.
  • binaries - the maximum number of bytes under which binary data are inlined in the response. This parameter is optional and is a single-value parameter.
  • childrenStart - a number representing the starting index where children are read from for each returned node. This parameter is optional and is a single-value parameter.
  • childrenCount - a number representing the maximum number of children to read for each returned node. This parameter is optional and is a single-value parameter.

In case of success, the server returns a 200 response with a content type of application/json. If the tree is not found at the specified revision (or at the latest revision), a 404 response is returned. If a revision is specified and that revision is not valid, a 410 response is returned. The body of the response is a JSON object representing a tree node.

A node is represented with a JSON object with the following structure:

{
  "properties": {
    "foo": {"type": "string", "value": "bar"},
    "baz": {"type": "uri", "value": "http://acme.com"},
    "qux": {"type": "booleans", "value": [true, false, true]}
  },
  "children": {
    "bob": null,
    "sue": null
  }

The response also contains a header called Oak-Revision containing the revision the tree is bound to. If a tree is read from the latest revision, the Oak-Revision header will contain the revision ID that was used on the server to read the returned tree. This allows the client to reuse that revision ID for subsequent reads. If a tree is read from a specified revision, the content of Oak-Revision simply contains the revision ID specified by the client. The Oak-Revision header is present in case of both a 200 and a 404 response, but it is not available in case of a 410 response.

Property values are serialized according to the following rules:

Repository type   JSON type   Type name         Description
===============   =========   =========         ===========
STRING            string      "string"
BINARY            string      "binary"          Binary value encoded in Base64.
BINARY            string      "binaryId"        Implementation-dependent reference to a binary value.
LONG              number      "long"
DOUBLE            number      "double"
DATE              number      "date"            Number of milliseconds since the epoch (Thu, 01 Jan 1970 00:00:00 GMT).
BOOLEAN           boolean     "boolean"
NAME              string      "name"
PATH              string      "path"
REFERENCE         string      "reference"
WEAK REFERENCE    string      "weakReference"
URI               string      "uri"
DECIMAL           string      "decimal"         String serialization of the decimal number.

Write content

PATCH /revisions/:revision/tree
PATCH /revisions/last/tree

The :revision part of the URL specifies the revision the patch should be applied to. A value for this parameter is obtained by using a dedicated method of this API. If the second kind of URL is used, where :revision assumes the value of last, the patch operation is performed on the latest state of the repository known to the server.

The request must have a content type of application/json and its body must contain the set of operations to apply to the content represented according to the following rules.

The set of operations must be contained inside a JSON array. The elements of this array are the operations. Every operation is represented by a JSON object. An operation can be of one of six different types: add, remove, set, unset, copy and move.

The following example patch shows all of the available operations and their serialization format.

[
  {
    "op": "add",
    "path": "/a/b/c",
    "properties": {
      "foo": {"type": "string", "value": "bar"},
      "baz": {"type": "uri", "value": "http://acme.com"},
      "qux": {"type": "booleans", "value": [true, false, true]}
    }
  },
  {
    "op": "remove",
    "path": "/a/b/c"
  },
  {
    "op": "set",
    "path": "/a/b/c",
    "name": "foo",
    "type": "string",
    "value": "bar"
  },
  {
    "op": "unset",
    "path": "/a/b/c",
    "name": "foo"
  },
  {
    "op": "move",
    "from": "/x/y",
    "to": "/p/q"
  },
  {
    "op": "copy",
    "from": "/x/y",
    "to": "/r/s"
  }
]

In case of success, the server returns a 201 response with a content type of application/json. The body of the respnose is a JSON object, where the revision is the value of the revision property.

{
  "revision": "..."
}

The value of the revision property is an opaque value whose format and semantics are implementation dependent.

Read a binary object

GET /binaries/:binaryId

If the binary object represented by :binaryId is found, the server returns a 200 response with a content type of application/octet-stream. The response body contains the whole content of the binary object.

If the client is not interested in the whole content of the binary object, a range request can be performed on the resource according to the rules specified in RFC7233. This has some implications on the remote API. In short:

  • when a GET or HEAD request on a binary resource is performed, the server should include a Accept-Ranges header in the response with the supported range types. If range request are supported, the server should support at least a bytes range type.
  • when a GET request is performed, the client can include a Range header in the request to select one or more ranges of the requested resource.
  • if one range is requested by the client, the server returns a 206 response with a Content-Range header specifying the portion of the resource that is transferred to the client.
  • if two or more ranges are requested by the client, the server returns a 206 response with a content type of multipart/byteranges. The response contains a multipart body, where each part represents one range as requested by the client. Every part has a content type of application/octet-stream and a Content-Range specifying which portion of the binary object the range represents.

More detailed information about range requests can be found in RFC7233.

Write a binary object

POST /binaries

The request should have a content type of application/octet-stream. The payload of the request will be saved by the repository in a binary object.

In case of success, the server will send back a 201 response with a content type of application/json. The payload of the response will contain the binary ID of the new binary object.

{
  "binaryId": "..."
}
GET /revisions/:revision/tree
GET /revisions/last/tree

The :revision part of the URL specifies the revision the search should be performed on. A value for this parameter is obtained by using a dedicated method of this API. If the second kind of URL is used, where :revision assumes the value of last, the search occurs on the latest state of the repository known to the server.

Request parameters:

  • query - the query statement to be executed. If the query statement contains placeholders, see below for a description on how to specify them as query parameters.
  • language - the language of the query. This parameter is necessary to define the syntax used in the provided query. A server, in fact, can support multiple query languages.
  • limit - the maximum number of rows included in the search results.
  • offset - the number of rows to skip from the returned search results.

In case of success, the server returns a 200 response with a content type of application/json. The body of the response is a JSON object similar to the following.

{
  "total": 2,
  "columns": [
    "c1", 
    "c2"
  ],
  "selectors": [
    "s1",
    "s2"
  ],
  "results": [
    {
      "columns": {"c1": "...", "c2": "..."}, 
      "selectors": {"s1": "...", "s2": "..."}
    },
    {
      "columns": {"c1": "...", "c2": "..."}, 
      "selectors": {"s1": "...", "s2": "..."}
    }
  ]
}

The values for the "columns" and "selectors" property are entirely dependent on the query. In example, the query select name from nt:unstructured as node will force the inclusion of the column name and the selector node in the search results. Please also note that special columns like jcr:path and jcr:score are always included in the results even if not specified in the query.

Since there some column and selector names may overlap, they are presented in two different sub-objects of each search result.

The value of each column in a search result is a value in the usual form:

{"type": "string", "value": "bar"}

Please note that some columns may be null. This happens when selecting a property name from a node that doesn't actually have a property of that name.

The value of each selector is a path. More specifically, it is the path of the node identified by the corresponding selector.

A more realistic example of a search result is provided below. These results are generated by the following query

select name, address from nt:unstructured node where jcr:path like '/test/%'  

For the query above, the returned search result (for my repository data) are:

{
   "total":2,
   "columns":[
      "name",
      "address",
      "jcr:path",
      "jcr:score"
   ],
   "selectors":[
      "node"
   ],
   "results":[
      {
         "columns":{
            "name":{
               "type":"string",
               "value":"Joe"
            },
            "address":{
               "type":"string",
               "value":"Whatever Street 42"
            },
            "jcr:path":{
               "type":"string",
               "value":"/test/two"
            },
            "jcr:score":null
         },
         "selectors":{
            "node":"/test/two"
         }
      },
      {
         "columns":{
            "name":{
               "type":"string",
               "value":"Moe"
            },
            "address":null,
            "jcr:path":{
               "type":"string",
               "value":"/test/one"
            },
            "jcr:score":null
         },
         "selectors":{
            "node":"/test/one"
         }
      }
   ]
}