The Digital Object Interface Protocol (DOIP) v2 is a specification of the DONA Foundation. Please click here for accessing the specification.
DOIP is a communication protocol that specifies how clients may interact with digital objects (DOs) that are managed by DOIP services. The method of such interaction is primarily using identifiers associated with digital objects, including those that represent operations, types, and clients. In this context, a DOIP service, such as a running Cordra instance, itself is considered a digital object. DOIP is an appropriate choice for users who are interested in an architectural style focused on invoking identified operations, or who focus on persistence or interoperability benefits.
In DOIP, each request represents a client invoking an operation on a target object. The client, operation, and target each have a unique persistent identifier.
This document does not describe further how the DOIP protocol works or the expected structure of digital objects, for which refer to the DOIP v2 specification. Instead this document describes how to invoke DOIP operations on Cordra-managed digital objects using examples.
See DOIP API for HTTP Clients for how to use the DOIP operations model in an HTTP context.
DOIP interface can be enabled via configuration in the Design Object. By default, a new Cordra instance will have a DOIP listener on port 9000, on the same listen address as the HTTP/HTTPS ports. This can be turned off or changed by editing the Design object.
Per DOIP, the DOIP service itself is sometimes the appropriate target for an operation. By default, the identifier for the DOIP service is the handle obtained by using the configured prefix from handleMintingConfig (see Handle Integration) together with the suffix “service”. A different serviceId can be configured as follows:
{
"doip": {
"enabled": true,
"port": 9000,
"processorConfig": {
"serviceId": "20.500.123/service"
}
}
}
This configuration should suffice for most uses. Information about other configuration options follows.
The primary address to which the DOIP interface should bind. Defaults to the same as the configured listenAddress for HTTP interfaces, which defaults to localhost.
An array of IP addresses to which the DOIP interface should bind. If listenAddress
is also specified, Cordra will
listen on all addresses specified.
The TCP port which the DOIP interface should bind to.
The backlog of incoming connections for the listening socket. Defaults to 50.
The maximum time for the server to wait for bytes from a client connection. Defaults to 5 minutes.
The maximum number of server threads for handling DOIP connections. In this experimental setting, defaults to 20.
The identity to use for the DOIP TLS certificate. Defaults to the same as processorConfig.serviceId. The TLS keys
can be configured via files in the Cordra data directory. By default the standard Cordra key pair files will be used
(privatekey
and publickey
); however, if you wish you can supply custom keys for the doip interface by adding files
named doipPrivateKey
and doipPublicKey
(in Handle format) to the data directory.
The identifier used for the target of operations intended for the DOIP service itself. Defaults to the configured handleMintingConfig.prefix together with the suffix “service”.
Commentary to be returned in the 0.DOIP/Op.Hello response.
The public address to advertise to clients via the 0.DOIP/Op.Hello operation. Defaults to the listenAddress.
The public port to advertise to clients via the 0.DOIP/Op.Hello operation. Defaults to the configured port.
The public key of the DOIP server to advertise to clients via the 0.DOIP/Op.Hello operation. In JWK format. Defaults to the public key used in the certificates.
A Cordra object has various components:
id
type
content
acl
metadata
payloads
The id and type map directly onto the corresponding properties of a Digital Object as defined in DOIP specification. The content, acl, and metadata properties of a Cordra object become attributes and the payloads become elements of the Digital Object.
In order to instantiate a Java DOIP client, you will need the doip-sdk jar file, along with the dependencies as listed
here. You may refer to
DOIP Client Library - Java Version for details on how to access the DOIP client library in Java. The jar files are also
included in the sw/lib
and sw/lib/cordra-client
directories of the Cordra distribution.
Instantiate net.cnri.doip.client.DoipClient
and call any of its connect
methods to create a
net.cnri.doip.client.DoipConnection
with a DOIP service. The methods of the DoipConnection
can then be used to
send DOIP requests and receive DOIP responses.
A DOIP service listener uses TLS, but many DOIP requests can be entered as plain text within a TLS session. To experiment with this on the command line, you can use for example:
openssl s_client -connect localhost:9000
Once connected, you can send requests and receive responses.
To briefly recap the DOIP specification, a DOIP message is a sequence of “segments” separated by newline # newline
,
and terminated by newline # newline # newline
. JSON segments contain JSON directly; bytes segments begin with
@ newline
, followed by multiple “chunks”, each of which is a decimal number indicating a number of bytes, followed
by that many bytes, followed by a newline. But many DOIP requests can be sent as JSON followed by two lines each with a
#
character. Some examples follow.
Request:
{
"targetId": "20.500.123/service",
"operationId": "0.DOIP/Op.Hello"
}
#
#
Response:
{
"status":"0.DOIP/Status.1",
"output":{
"id":"20.500.123/service",
"type":"0.TYPE/DOIPServiceInfo",
"attributes":{
"ipAddress":"127.0.0.1",
"port":9000,
"protocol":"TCP",
"protocolVersion":"2.0",
"publicKey":{
"kty":"RSA",
"n":"m2MIsyH7F7NMA9EABMfPjzbid3MIh9vTP28MKVKFN2waUnPlsb_JM9OfE0cwyRUXuehuNUm7CbaQmOINOFsQhoQBGyj12TnC_Lm__Rgf7Shvl0xKFr83YTa7Zw7HWqOMb_4kY2O7OdV98RIc6oD62cY7j1E_fiudzOnFh5SaXvP3qS3OrNrOA4gODQdplhNikwP5_VwCA45lDnfVBO2Dj62oFl55-BeIc1YQoJ_kkN-8JbNsd3kGKZnq7VDSrGfLAyLLyML9dE7jRK3qxR5Ok_va49KGvQV-krssyacBAIVk1zBUQ8lFnxBcH6g_0Hl_h_zcv-jtfeCCCoZ4sB46Hw==",
"e":"AQAB"
}
}
}
}
#
#
Request:
{
"targetId": "20.500.123/service",
"operationId": "0.DOIP/Op.ListOperations"
}
#
#
Response:
{
"status":"0.DOIP/Status.1",
"output":["0.DOIP/Op.Hello","0.DOIP/Op.ListOperations","0.DOIP/Op.Create","0.DOIP/Op.Search"]
}
#
#
Request:
{
"targetId": "20.500.123/service",
"operationId": "0.DOIP/Op.Search",
"attributes": {
"query": "+type:Schema +/name:User"
}
}
#
#
Response:
{"status":"0.DOIP/Status.1"}
#
{
"size": 1,
"results": [
{
"id": "test/ccf24d69f39aafee2195",
"type": "Schema",
"attributes": {
"content": {
"identifier": "test/ccf24d69f39aafee2195",
"name": "User",
"schema": {
...
}
}
}
}
]
}
#
#
In general a creation request must specify the “type” and an “attribute” called “content”. If you wish to specify the id of the newly created object, specify an “id” as a sibling property of “type” and “attributes”.
Request:
{
"targetId": "20.500.123/service",
"operationId": "0.DOIP/Op.Create",
"input": {
"type": "User",
"attributes": {
"content": {
"username": "user",
"password": "password"
}
}
},
"authentication": {
"username": "admin",
"password": "password"
}
}
#
#
Response:
{
"status":"0.DOIP/Status.1",
"output":{
"id":"test/12dea96fec20593566ab",
"type":"User",
"attributes":{
"content":{
"id":"test/12dea96fec20593566ab",
"username":"user",
"password":""
},
"metadata":{
"createdOn":1537467895407,
"createdBy":"admin",
"modifiedOn":1537467895450,
"modifiedBy":"admin",
"txnId":6
}
},
"elements":[]
}
}
#
#
Request:
{
"clientId": "test/12dea96fec20593566ab",
"targetId": "20.500.123/service",
"operationId": "0.DOIP/Op.Create",
"authentication": {
"password": "password"
}
}
#
{
"type": "Document",
"attributes": {
"content": {
"id": "",
"name": "Hello World"
}
},
"elements": [
{
"id": "file",
"type": "text/plain",
"attributes": {
"filename": "helloworld.txt"
}
}
]
}
#
{"id":"file"}
#
@
12
Hello World
#
#
Response:
{
"status":"0.DOIP/Status.1",
"output":{
"id":"test/0a4d55a8d778e5022fab",
"type":"Document",
"attributes":{
"content":{
"id":"test/0a4d55a8d778e5022fab",
"name":"Hello World"
},
"metadata":{
"createdOn":1537469656224,
"createdBy":"test/12dea96fec20593566ab",
"modifiedOn":1537469656235,
"modifiedBy":"test/12dea96fec20593566ab",
"txnId":7
}
},
"elements":[
{
"id":"file",
"length":0,
"type":"text/plain",
"attributes":{
"filename":"helloworld.txt"
}
}
]
}
}
#
#
Request:
{
"targetId": "test/0a4d55a8d778e5022fab",
"operationId": "0.DOIP/Op.Retrieve"
}
#
#
Response:
{
"status":"0.DOIP/Status.1",
"output":{
"id":"test/0a4d55a8d778e5022fab",
"type":"Document",
"attributes":{
"content":{
"id":"test/0a4d55a8d778e5022fab",
"name":"Hello World"
},
"metadata":{
"createdOn":1537469656224,
"createdBy":"test/12dea96fec20593566ab",
"modifiedOn":1537469656235,
"modifiedBy":"test/12dea96fec20593566ab",
"txnId":7
}
},
"elements":[
{
"id":"file",
"length":0,
"type":"text/plain",
"attributes":{
"filename":"helloworld.txt"
}
}
]
}
}
#
#
Request:
{
"targetId": "test/0a4d55a8d778e5022fab",
"operationId": "0.DOIP/Op.Retrieve",
"attributes": {
"element": "file"
}
}
#
#
Response:
{"status":"0.DOIP/Status.1"}
#
@
12
Hello World
#
#
Cordra enables developers to extend the core functionality and expose that as extended operations. See Type Methods for how to write extended operations.
In the case of functionality that is added as an instance Type method, the targetId of the DOIP operation will be digital object identifier to which this instance Type method should be applied. In the case of any static Type method, the targetId will be the digital object identifier of the Type. In either case, the operationId is the identifier that is specified in the JavaScript export statement associated with the method definition.
Below you will find the DOIP request and response structures that applies to the static Type method defined here.
Request:
{
"targetId": "test/7060f82cc15962ba4851",
"operationId": "123/abc",
"authentication": { "username": "admin", "password": "password" },
"input": {"foo":"hello", "bar":"world"}
}
#
#
Here test/7060f82cc15962ba4851 is the identifier of the Type digital object on which the static Type method is defined.
Response:
{
"status":"0.DOIP/Status.001",
"output":{"input":{"foo":"hello","bar":"world"},"timestamp":1568752848904}
}
#
#