Skip to content

Commit 1431b9d

Browse files
timea-solidacoburn
andauthored
JCL-306: added example for working with nested Solid resources (#404)
* added example for working with nexted Solid resources * further distinguish between non RDF and RDF resources * Update src/site/apt/data-modeling/multiple-resources.apt.vm Co-authored-by: Aaron Coburn <aaronc@inrupt.com>
1 parent dfa204e commit 1431b9d

File tree

5 files changed

+230
-156
lines changed

5 files changed

+230
-156
lines changed

src/site/apt/data-modeling.apt.vm

Lines changed: 9 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Data Modeling
44

55
* What you will need
66

7+
* {{{https://start.inrupt.com/profile}A Solid Pod}}
8+
79
* About 15 minutes
810

911
* Your favorite text editor or IDE
@@ -14,162 +16,14 @@ Data Modeling
1416

1517
* How to complete
1618

17-
The solution involves creating a wrapper class so that your class can act not only as an RDF Graph
18-
but also as a domain-specific type.
19-
20-
* 1. Subclass the RDFSource type
21-
22-
The first step involves ensuring that your class extends the <<<com.inrupt.client.RDFSource>>> class.
23-
Afterwards, consider the methods you expect in your class.
24-
25-
+---
26-
import com.inrupt.client.RDFSource;
27-
import java.net.URI;
28-
import java.util.Set;
29-
import org.apache.commons.rdf.api.Dataset;
30-
31-
public class Playlist extends RDFSource {
32-
33-
public Playlist(URI identifier, Dataset dataset) {
34-
super(identifier, dataset);
35-
}
36-
37-
public String getTitle() {
38-
...
39-
}
40-
41-
public void setTitle(String title) {
42-
...
43-
}
44-
45-
public Set<URI> getSongs() {
46-
...
47-
}
48-
49-
}
50-
+---
51-
52-
53-
* 2. Create the wrapper node
54-
55-
Next, you will create an inner class that binds the data between RDF and your class.
56-
57-
+---
58-
import com.inrupt.client.RDFSource;
59-
import com.inrupt.rdf.wrapping.commons.TermMappings;
60-
import com.inrupt.rdf.wrapping.commons.ValueMappings;
61-
import com.inrupt.rdf.wrapping.commons.WrapperIRI;
62-
import java.net.URI;
63-
import java.util.Set;
64-
import org.apache.commons.rdf.api.Dataset;
65-
import org.apache.commons.rdf.api.Graph;
66-
import org.apache.commons.rdf.api.IRI;
67-
import org.apache.commons.rdf.api.RDFTerm;
68-
69-
public class Playlist extends RDFSource {
70-
71-
...
72-
73-
static IRI DC_TITLE = rdf.createIRI("http://purl.org/dc/terms/title");
74-
static IRI EX_SONG = rdf.createIRI("http://music.example/song");
75-
76-
class Node extends WrapperIRI {
77-
Node(RDFTerm original, Graph graph) {
78-
super(original, graph);
79-
}
80-
81-
String getTitle() {
82-
return anyOrNull(DC_TITLE, ValueMappings::literalAsString);
83-
}
84-
85-
void setTitle(String value) {
86-
overwriteNullable(DC_TITLE, value, TermMappings::asStringLiteral);
87-
}
88-
89-
Set<URI> getSongs() {
90-
return objects(EX_SONG, TermMappings::asIri, ValueMappings::iriAsUri);
91-
}
92-
93-
}
94-
}
95-
+---
96-
97-
Your inner class (<<<Node>>> in this example) will have access to a variety of functions
98-
to aid in mapping between the underlying RDF Graph and your Java type. {{{https://javadoc.io/doc/com.inrupt/inrupt-rdf-wrapping-commons/latest/com/inrupt/rdf/wrapping/commons/WrapperBlankNodeOrIRI.html}These methods}} are summarized in the <<<inrupt-rdf-wrapping-commons>>> Javadocs.
99-
100-
Similarly, the built-in {{{https://javadoc.io/doc/com.inrupt/inrupt-rdf-wrapping-commons/latest/com/inrupt/rdf/wrapping/commons/TermMappings.html}TermMappings}}
101-
and {{{https://javadoc.io/doc/com.inrupt/inrupt-rdf-wrapping-commons/latest/com/inrupt/rdf/wrapping/commons/ValueMappings.html}ValueMappings}}
102-
are described in the {{{https://javadoc.io/doc/com.inrupt/inrupt-rdf-wrapping-commons/latest/com/inrupt/rdf/wrapping/commons/package-summary.html}Javadocs}} for the <<<inrupt-rdf-wrapping-commons>>> library.
103-
104-
* 3. Connect your public methods to the wrapper node
105-
106-
Now you are ready to link your public methods to the methods from your inner class.
107-
108-
+---
109-
import com.inrupt.client.RDFSource;
110-
import com.inrupt.rdf.wrapping.commons.TermMappings;
111-
import com.inrupt.rdf.wrapping.commons.ValueMappings;
112-
import com.inrupt.rdf.wrapping.commons.WrapperIRI;
113-
import java.net.URI;
114-
import java.util.Set;
115-
import org.apache.commons.rdf.api.Dataset;
116-
import org.apache.commons.rdf.api.Graph;
117-
import org.apache.commons.rdf.api.IRI;
118-
import org.apache.commons.rdf.api.RDFTerm;
119-
120-
public class Playlist extends RDFSource {
121-
122-
private Node subject;
123-
124-
public Playlist(URI identifier, Dataset dataset) {
125-
super(identifier, dataset);
126-
127-
subject = new Node(rdf.createIRI(identifier.toString()), getGraph());
128-
}
129-
130-
public String getTitle() {
131-
return subject.getTitle();
132-
}
133-
134-
public void setTitle(String title) {
135-
subject.setTitle(title);
136-
}
137-
138-
public Set<URI> getSongs() {
139-
return subject.getSongs();
140-
}
141-
142-
...
143-
144-
+---
145-
146-
* 4. Use your class in the high-level client
147-
148-
Now, you can use your class with the high-level Solid client, mapping data seamlessly between your
149-
Java application and an RDF resource.
150-
151-
+---
152-
import com.inrupt.client.auth.Session;
153-
import com.inrupt.client.openid.OpenIdSession;
154-
import java.net.URI;
155-
156-
public class MyApplication {
157-
158-
public void run() {
159-
Session session = OpenIdSession.ofClientCredentials(issuer, clientId, clientSecret, "client_secret_basic");
160-
SolidSyncClient client = SolidSyncClient.getClient().session(session);
19+
At first you need to know (or choose) how your data is modeled on the Solid Pod.
20+
Than one can model your data wite the available domain-specific types of with the class wrapper.
16121

162-
URI uri = URI.create("https://storage.example/playlists/910b509c-1ca5-4d6a-a093-377e8e1b390e");
163-
try (Playlist playlist = client.read(uri, Playlist.class)) {
164-
System.out.println(playlist.getTitle());
22+
* Different data model approaches
16523

166-
// Set the playlist title
167-
playlist.setTitle(title);
24+
Let's work with an example to describe the data model options.
16825

169-
// Update the playlist in your storage
170-
client.update(playlist);
171-
}
172-
}
173-
}
174-
+---
26+
We want to model a Book Library on the Pod:
27+
* One Solid Resource - we have a Book Library Solid Resource which contains all the book descriptions part of that library it one Solid Resource or
28+
* Multiple Solid Resources - we have a Book Library Solid Container which holds more Solid Resources. Each Resource, in turn, contains the description of only one Book.
17529

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
Data Modeling for multiple Solid Resources
2+
3+
* How to complete
4+
5+
The solution involves working with domain-specific type such as <<<SolidContainer>>> and <<<SolidResource>>>.
6+
7+
* Working with Solid Containers
8+
9+
We have to access the contained resources of a Solid Container. We assume that all Solid RDF Resources are Books.
10+
11+
You can use a wrapper class (describe on the {{{./one-resource.html}One Solid Resource}} page) with the high-level Solid client, mapping data seamlessly between your Java application and an RDF resource.
12+
13+
+---
14+
import com.inrupt.client.auth.Session;
15+
import com.inrupt.client.openid.OpenIdSession;
16+
import com.inrupt.client.solid.SolidContainer;
17+
import java.net.URI;
18+
19+
public class MyApplication {
20+
21+
public void run() {
22+
Session session = OpenIdSession.ofClientCredentials(issuer, clientId, clientSecret, "client_secret_basic");
23+
SolidSyncClient client = SolidSyncClient.getClient().session(session);
24+
25+
URI uri = URI.create("https://storage.example/booklibrary/");
26+
try (final SolidContainer container = client.read(uri, SolidContainer.class)) {
27+
final var resources = container.getResources();
28+
resources.forEach(r -> {
29+
if (!(r.getIdentifier().toString().endsWith("/"))) { //if it ends in a '/' it is a container
30+
final Request req = Request.newBuilder(r.getIdentifier())
31+
.HEAD()
32+
.build();
33+
final var contentType = req.headers().firstValue("Content-Type");
34+
if (contentType.isPresent() && (contentType.get().toLowerCase().contains("text/turtle"))) {
35+
try (Playlist playlist = client.read(r.getIdentifier(), Playlist.class)) {
36+
System.out.println(playlist.getTitle());
37+
38+
// Set the playlist title
39+
playlist.setTitle(title);
40+
41+
// Update the playlist in your storage
42+
client.update(playlist);
43+
}
44+
}
45+
}
46+
});
47+
}
48+
49+
}
50+
}
51+
+---
52+
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
Data Modeling for one Solid Resource
2+
3+
* How to complete
4+
5+
The solution involves creating a wrapper class so that your class can act not only as an RDF Graph
6+
but also as a domain-specific type.
7+
8+
* 1. Subclass the SolidRDFSource type
9+
10+
The first step involves ensuring that your class extends the <<<com.inrupt.client.SolidRDFSource>>> class.
11+
Afterwards, consider the methods you expect in your class.
12+
13+
+---
14+
import com.inrupt.client.SolidRDFSource;
15+
import java.net.URI;
16+
import java.util.Set;
17+
import org.apache.commons.rdf.api.Dataset;
18+
19+
public class Playlist extends SolidRDFSource {
20+
21+
public Playlist(URI identifier, Dataset dataset) {
22+
super(identifier, dataset);
23+
}
24+
25+
public String getTitle() {
26+
...
27+
}
28+
29+
public void setTitle(String title) {
30+
...
31+
}
32+
33+
public Set<URI> getSongs() {
34+
...
35+
}
36+
37+
}
38+
+---
39+
40+
41+
* 2. Create the wrapper node
42+
43+
Next, you will create an inner class that binds the data between RDF and your class.
44+
45+
+---
46+
import com.inrupt.client.SolidRDFSource;
47+
import com.inrupt.rdf.wrapping.commons.TermMappings;
48+
import com.inrupt.rdf.wrapping.commons.ValueMappings;
49+
import com.inrupt.rdf.wrapping.commons.WrapperIRI;
50+
import java.net.URI;
51+
import java.util.Set;
52+
import org.apache.commons.rdf.api.Dataset;
53+
import org.apache.commons.rdf.api.Graph;
54+
import org.apache.commons.rdf.api.IRI;
55+
import org.apache.commons.rdf.api.RDFTerm;
56+
57+
public class Playlist extends SolidRDFSource {
58+
59+
...
60+
61+
static IRI DC_TITLE = rdf.createIRI("http://purl.org/dc/terms/title");
62+
static IRI EX_SONG = rdf.createIRI("http://music.example/song");
63+
64+
class Node extends WrapperIRI {
65+
Node(RDFTerm original, Graph graph) {
66+
super(original, graph);
67+
}
68+
69+
String getTitle() {
70+
return anyOrNull(DC_TITLE, ValueMappings::literalAsString);
71+
}
72+
73+
void setTitle(String value) {
74+
overwriteNullable(DC_TITLE, value, TermMappings::asStringLiteral);
75+
}
76+
77+
Set<URI> getSongs() {
78+
return objects(EX_SONG, TermMappings::asIri, ValueMappings::iriAsUri);
79+
}
80+
81+
}
82+
}
83+
+---
84+
85+
Your inner class (<<<Node>>> in this example) will have access to a variety of functions
86+
to aid in mapping between the underlying RDF Graph and your Java type. {{{https://docs.inrupt.com/developer-tools/api/java/inrupt-client/latest/com/inrupt/rdf/wrapping/commons/package-summary.html}These methods}} are summarized in the <<<inrupt-rdf-wrapping-commons>>> Javadocs.
87+
88+
Similarly, the built-in {{{https://docs.inrupt.com/developer-tools/api/java/inrupt-client/latest/com/inrupt/rdf/wrapping/commons/TermMappings.html}TermMappings}}
89+
and {{{https://docs.inrupt.com/developer-tools/api/java/inrupt-client/latest/com/inrupt/rdf/wrapping/commons/ValueMappings.html}ValueMappings}}
90+
are described in the {{{https://docs.inrupt.com/developer-tools/api/java/inrupt-client/latest/com/inrupt/rdf/wrapping/commons/package-summary.html}Javadocs}} for the <<<inrupt-rdf-wrapping-commons>>> library.
91+
92+
* 3. Connect your public methods to the wrapper node
93+
94+
Now you are ready to link your public methods to the methods from your inner class.
95+
96+
+---
97+
import com.inrupt.client.SolidRDFSource;
98+
import com.inrupt.rdf.wrapping.commons.TermMappings;
99+
import com.inrupt.rdf.wrapping.commons.ValueMappings;
100+
import com.inrupt.rdf.wrapping.commons.WrapperIRI;
101+
import java.net.URI;
102+
import java.util.Set;
103+
import org.apache.commons.rdf.api.Dataset;
104+
import org.apache.commons.rdf.api.Graph;
105+
import org.apache.commons.rdf.api.IRI;
106+
import org.apache.commons.rdf.api.RDFTerm;
107+
108+
public class Playlist extends SolidRDFSource {
109+
110+
private Node subject;
111+
112+
public Playlist(URI identifier, Dataset dataset) {
113+
super(identifier, dataset);
114+
115+
subject = new Node(rdf.createIRI(identifier.toString()), getGraph());
116+
}
117+
118+
public String getTitle() {
119+
return subject.getTitle();
120+
}
121+
122+
public void setTitle(String title) {
123+
subject.setTitle(title);
124+
}
125+
126+
public Set<URI> getSongs() {
127+
return subject.getSongs();
128+
}
129+
130+
...
131+
132+
+---
133+
134+
* 4. Use your class in the high-level client
135+
136+
Now, you can use your class with the high-level Solid client, mapping data seamlessly between your
137+
Java application and an RDF resource.
138+
139+
+---
140+
import com.inrupt.client.auth.Session;
141+
import com.inrupt.client.openid.OpenIdSession;
142+
import java.net.URI;
143+
144+
public class MyApplication {
145+
146+
public void run() {
147+
Session session = OpenIdSession.ofClientCredentials(issuer, clientId, clientSecret, "client_secret_basic");
148+
SolidSyncClient client = SolidSyncClient.getClient().session(session);
149+
150+
URI uri = URI.create("https://storage.example/playlists/910b509c-1ca5-4d6a-a093-377e8e1b390e");
151+
try (Playlist playlist = client.read(uri, Playlist.class)) {
152+
System.out.println(playlist.getTitle());
153+
154+
// Set the playlist title
155+
playlist.setTitle(title);
156+
157+
// Update the playlist in your storage
158+
client.update(playlist);
159+
}
160+
}
161+
}
162+
+---
163+

0 commit comments

Comments
 (0)