Using Virtuoso as LDP Client and Server

Using Virtuoso as LDP Client and Server

What is the Linked Data Platform (LDP)?

Use of HTTP to Create, Read, Update, and Delete Linked Data Resources (Documents) that are part of a collection (folder). Naturally, this kind of task posses the following questions and associated challenges:

  • Document Resource Issues
    • What resource content notations and across-the-wire content serialization formats should be used?
    • How is collision detection for updates handled with maximum concurrent (i.e. optimistically)?
    • How do client handle changes to associated resources, such as content type changes?
    • How servers ease the burden of constraints associated with resource creation?
  • Container/Folder Resource Issues
    • To which address/location (URL) on an HTTP network can I POST my resource member or container creation requests?
    • How do I GET (retrieve) a list of existing member resources associated with a container?
    • How is container ordering of member resources expressed and represented?
    • How do I obtain metadata about the resources that are members of a container, and the container resource itself?
    • How do I GET retrieve resources that are members of a large container, using paging?
    • How do I deal with metadata querying scoped to member resources and container resources?

The Linked Data Platform (LDP) was developed by W3C members to answer many of these questions.

Why is the LDP important?

It formalizes Linked Data deployment and use by standardizing the representation and behavior of, and the generation and processing of HTTP requests regarding, Linked Data Platform Resources (LDPRs) and Linked Data Platform Containers (LDPCs). Using the Linked Data Platform thereby increases availability and accessibility of Linked Data on the Web.

How to use Virtuoso’s LDP features

Virtuoso’s LDP functionality is a built-in, integral part of the product.

Virtuoso operates as an LDP Client, generating HTTP requests and processing HTTP responses that conform to the rules defined for LDPRs and LDPCs, when it is operating against LDP Servers.

Virtuoso also operates as an LDP Server, by processing HTTP requests and generating HTTP responses that conform to the rules defined for LDPRs and LDPCs. The following examples use the command line utility curl to demonstrate Virtuoso’s LDP Server implementation.

Example 1: How to enable LDP on a folder/collection

The Virtuoso Conductor can be used to enable LDP on a WebDAV folder/collection of resources as follows

One could use the following options to enable LDP on a given folder/collection of resources:

  1. Log in to the conductor at http://host:port/conductor and go to Web Application ServerContent Management:
  2. Provide a location path for the destination folder/collection to be LDP enabled, in our example we will enable the demo users Public folder:
  3. Click on Action column Update Properties icon for the Public folder:
  4. Select the LDP enable/disable checkbox option to enable the folder for LDP use:
  5. Click the Update button to save the change.

Note, the LDP property can also be set from command line using the DB.DBA.DAV_PROP_SET() procedure, with:

SQL> DB.DBA.DAV_PROP_SET ('/DAV/home/demo/Public/', 'LDP', 'ldp:BasicContainer', 1, 'dav','dav');

Done. -- 0 msec.
SQL>

Example 2: Create and verify a simple LDPR

  1. Write a bit of text (“This content is not Turtle.” in this example) to a text file (“test2.txt”) in DAV:
curl -iX PUT -H "Content-Type: text/plain" -u dav:dav -d 'This content is not Turtle.' "http://localhost:8890/DAV/home/demo/Public/test2.txt"
  1. The server response should be of the form:
$ curl -iX PUT -H "Content-Type: text/plain" -u dav:dav -d 'This content is not Turtle.' "http://localhost:8890/DAV/home/demo/Public/test2.txt"
HTTP/1.1 201 Created
Server: Virtuoso/07.20.3238 (Linux) x86_64-pc-linux-gnu  
Connection: close
Date: Fri, 01 Dec 2023 12:04:26 GMT
Accept-Ranges: bytes
Location: http://localhost:8890/DAV/home/demo/Public/test2.txt
Allow: COPY, DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PATCH, POST, PROPFIND, PROPPATCH, PUT, TRACE, UNLOCK
Vary: Accept-Encoding, Access-Control-Request-Headers, Origin
MS-Author-Via: DAV,SPARQL
Accept-Patch: application/sparql-update
Accept-Post: text/turtle, text/n3, text/nt, text/html, application/ld+json
Link: <http://www.w3.org/ns/ldp#Resource>; rel="type"
Link: <http://www.w3.org/ns/ldp#NonRDFSource>; rel="type"
Link: <http://localhost:8890/DAV/home/demo/Public/test2.txt,meta>; rel="describedby"
Link: <http://localhost:8890/DAV/home/demo/Public/test2.txt,meta>; rel="meta"; title="Metadata File"
ETag: "0b63097a2be403d0f0d7d9fbcedd42bb"
Content-Type: text/plain
Content-Length: 189

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><HTML><HEAD><TITLE>201 Created</TITLE></HEAD><BODY><H1>Created</H1>Resource /DAV/home/demo/Public/test2.txt has been created.</BODY></HTML>
$
  1. Confirm that the server took the submission as LDP data with the commands:
  • Using content negotiation to get RDF(Turtle):
curl -iHAccept:text/turtle http://localhost:8890/DAV/home/demo/Public/test2.txt
  • The server response should be of the form:
$ curl -iHAccept:text/turtle http://localhost:8890/DAV/home/demo/Public/test2.txt
HTTP/1.1 200 OK
Server: Virtuoso/07.20.3238 (Linux) x86_64-pc-linux-gnu  
Connection: Keep-Alive
Date: Fri, 01 Dec 2023 12:21:03 GMT
Accept-Ranges: bytes
Allow: COPY, DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PATCH, POST, PROPFIND, PROPPATCH, PUT, TRACE, UNLOCK
Vary: Accept-Encoding, Access-Control-Request-Headers, Origin
MS-Author-Via: DAV,SPARQL
Accept-Patch: application/sparql-update
Accept-Post: text/turtle, text/n3, text/nt, text/html, application/ld+json
Link: <http://www.w3.org/ns/ldp#Resource>; rel="type"
Link: <http://www.w3.org/ns/ldp#NonRDFSource>; rel="type"
Link: <http://localhost:8890/DAV/home/demo/Public/test2.txt,meta>; rel="describedby"
Link: <?p=1>; rel="first"
Link: <?p=1>; rel="last"
Link: <http://localhost:8890/DAV/home/demo/Public/test2.txt,meta>; rel="meta"; title="Metadata File"
ETag: "0b63097a2be403d0f0d7d9fbcedd42bb"
X-SPARQL-default-graph: http://hfw.openlinksw.com:8890/DAV/home/demo/Public/test2.txt
Content-disposition: filename=sparql_2023-12-01_12-21-03Z.ttl
Content-Type: text/turtle
Content-Length: 257

@prefix rdf:	<http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
@prefix ldp:	<http://www.w3.org/ns/ldp#> .

<http://localhost:8890/DAV/home/demo/Public/test2.txt>
	rdf:type	rdfs:Resource , ldp:Resource .
$
  • Without content negotiation ie native to the resource:
$ curl -i http://localhost:8890/DAV/home/demo/Public/test2.txt && echo
  • The server response should be of the form:
$ curl -i http://localhost:8890/DAV/home/demo/Public/test2.txt && echo
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 27

This content is not Turtle.
$

Example 3: Create an LDPR from an existing text file containing Turtle data

  1. Assuming a file named ldp-books.ttl with the following Turtle content –
@prefix    :  <http://example.org/book/> .
@prefix  ns:  <http://example.org/ns#>   .

:book1  <http://purl.org/dc/elements/1.1/title>  "LDP Tutorial"
   ;                                   ns:price  42
   ;                                ns:discount  0.2
   .

:book2  <http://purl.org/dc/elements/1.1/title>  "The Semantic Web"
   ;                                   ns:price  23
   ;                                ns:discount  0.25 
   .
curl -X PUT --data-binary @./ldp-books.ttl  -iH "Content-Type: text/turtle" -u dav:dav "http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl"
  1. The server response should be of the form:
$ curl -X PUT --data-binary @./ldp-books.ttl  -iH "Content-Type: text/turtle" -u dav:dav "http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl"
HTTP/1.1 201 Created
Server: Virtuoso/07.20.3238 (Linux) x86_64-pc-linux-gnu  
Connection: close
Date: Fri, 01 Dec 2023 10:15:21 GMT
Accept-Ranges: bytes
Location: http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl
Allow: COPY, DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PATCH, POST, PROPFIND, PROPPATCH, PUT, TRACE, UNLOCK
Vary: Accept-Encoding, Access-Control-Request-Headers, Origin
MS-Author-Via: DAV,SPARQL
Accept-Patch: application/sparql-update
Accept-Post: text/turtle, text/n3, text/nt, text/html, application/ld+json
Link: <http://www.w3.org/ns/ldp#Resource>; rel="type"
Link: <http://www.w3.org/ns/ldp#RDFSource>; rel="type"
Link: <http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl,meta>; rel="meta"; title="Metadata File"
ETag: "66a4b9f4bdf2990f914ffc08e1d7ab5f"
Content-Type: text/turtle
Content-Length: 193

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><HTML><HEAD><TITLE>201 Created</TITLE></HEAD><BODY><H1>Created</H1>Resource /DAV/home/demo/Public/ldp-books.ttl has been created.</BODY></HTML>
  1. Confirm that the server took the submission as LDP data with the command:
curl -HAccept:text/turtle http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl
  1. The server response should be of the form:
$ curl -HAccept:text/turtle http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl
@prefix ns0:	<http://example.org/ns#> .
@prefix ns1:	<http://example.org/book/> .
@prefix dc:	<http://purl.org/dc/elements/1.1/> .
@prefix rdf:	<http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix ldp:	<http://www.w3.org/ns/ldp#> .
@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .

ns1:book1
	ns0:price	42 ;
	ns0:discount	0.2 ;
	dc:title	"LDP Tutorial" .
ns1:book2
	ns0:price	23 ;
	ns0:discount	0.25 ;
	dc:title	"The Semantic Web" .
<http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl>
	rdf:type	ldp:RDFSource , rdfs:Resource , ldp:Resource .

Example 4: Access Resources in an LDPC (e.g., a DAV folder)

  1. Request all LDP data for an LDPC, such as the DAV/home/demo/Public folder, to be returned as Turtle –
curl -HAccept:text/turtle http://localhost:8890/DAV/home/demo/Public/
  1. The server response should be of the form:
$ curl -HAccept:text/turtle http://localhost:8890/DAV/home/demo/Public/
@prefix rdf:	<http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix ns1:	<http://www.w3.org/ns/iana/media-types/text/plain#> .
@prefix xsd:	<http://www.w3.org/2001/XMLSchema#> .
@prefix stat:	<http://www.w3.org/ns/posix/stat#> .
@prefix ns4:	<http://www.w3.org/ns/iana/media-types/text/turtle#> .
@prefix ldp:	<http://www.w3.org/ns/ldp#> .

<http://localhost:8890/DAV/home/demo/Public/test2.txt>
	rdf:type	ns1:Resource ;
	stat:mtime	"2023-12-01T12:04:26.441709"^^xsd:dateTime ;
	stat:size	27 .
<http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl>
	rdf:type	ns4:Resource ;
	stat:mtime	"2023-12-01T10:15:21.571913"^^xsd:dateTime ;
	stat:size	442 .
<http://localhost:8890/DAV/home/demo/Public/>
	rdf:type	ldp:BasicContainer , ldp:Container ;
	ldp:contains	<http://localhost:8890/DAV/home/demo/Public/test2.txt> , <http://localhost:8890/DAV/home/demo/Public/ldp-books.ttl> .
$

Example 5: Delete Resources in an LDPC (e.g., a DAV folder)

  1. Delete a resource from the LDPC with the command:
curl -iX DELETE http://localhost:8890/DAV/home/demo/Public/test2.txt -u dav:dav
  1. The server response should be of the form:
$ curl -iX DELETE http://localhost:8890/DAV/home/demo/Public/test2.txt -u dav:dav
HTTP/1.1 204 No Content
Server: Virtuoso/07.20.3238 (Linux) x86_64-pc-linux-gnu  
Connection: Keep-Alive
Content-Type: text/html; charset=UTF-8
Date: Fri, 01 Dec 2023 12:39:59 GMT
Accept-Ranges: bytes
Allow: COPY, DELETE, GET, HEAD, LOCK, MKCOL, MOVE, OPTIONS, PATCH, POST, PROPFIND, PROPPATCH, PUT, TRACE, UNLOCK
Vary: Accept-Encoding, Access-Control-Request-Headers, Origin
MS-Author-Via: DAV
Accept-Post: */*
Link: <http://localhost:8890/DAV/home/demo/Public/test2.txt,meta>; rel="meta"; title="Metadata File"
Content-Length: 0
$

Example 6: Creating LDPR via POST requests

  1. Using an explicit target
$ curl -iX POST -H "Content-Type: text/turtle" -u dav:***-d '<#metoo> <#nick> "metoo_nick" .' "http://localhost:8890/DAV/home/demo/Public/test3.ttl"  
HTTP/1.1 201 Created
Location: http://localhost:8890/DAV/home/demo/Public/test3.ttl
  1. Using a suggested prefix Slug HTTP header
$ curl -iX POST -H "Content-Type: text/turtle"  -H"Slug: text" -u dav:*** -d '<#me> <#nick> "my_nick" .' "http://localhost:8890/DAV/home/demo/Public/" 
HTTP/1.1 201 Created
Location: http://localhost:8890/DAV/home/demo/Public/text-eaa5

Note: LDP also supports JSON-LD as an alternative to Turtle, to use it replace the text/turtle media type with application/ld+json and the Content-Type and use relevant documents as the input in the cURL examples above.

Related