| 1 |
9486 |
james_leigh |
----------------------------------------
|
| 2 |
|
james_leigh |
Object Repository
|
| 3 |
|
james_leigh |
----------------
|
| 4 |
|
james_leigh |
James Leigh
|
| 5 |
|
james_leigh |
----------------
|
| 6 |
11597 |
james_leigh |
Feb 2012
|
| 7 |
9423 |
james_leigh |
|
| 8 |
9486 |
james_leigh |
|
| 9 |
9419 |
james_leigh |
Object Repository
|
| 10 |
|
james_leigh |
|
| 11 |
|
james_leigh |
The Object Repository is an extension to the Sesame RDF Repository that
|
| 12 |
|
james_leigh |
allows an RDF store to function as an object store. It maps Java objects
|
| 13 |
|
james_leigh |
to and from RDF resources and OWL classes to Java classes in a
|
| 14 |
|
james_leigh |
non-intrusive manner that enables developers to work with resources
|
| 15 |
11450 |
james_leigh |
stored in an RDF Repository as objects. The Object Repository may also
|
| 16 |
|
james_leigh |
optionally be configured with a BLOB store, to store information-resources.
|
| 17 |
9419 |
james_leigh |
|
| 18 |
|
james_leigh |
Sesame Repositories can be created using the console. Use the connect
|
| 19 |
|
james_leigh |
command to set the data directory before creating a repository using the
|
| 20 |
|
james_leigh |
create command. Once the repository has been created it can be accessed
|
| 21 |
|
james_leigh |
in Java through the RepositoryProvider's getRepositoryManager(dataDir)
|
| 22 |
|
james_leigh |
method, which takes the URI of the directory location that was used in
|
| 23 |
|
james_leigh |
the connect command of the console. Then the repository can be accessed
|
| 24 |
|
james_leigh |
using the getRepository(id) method of the returned RepositoryManager.
|
| 25 |
|
james_leigh |
|
| 26 |
|
james_leigh |
The ObjectRepository must be created through the
|
| 27 |
|
james_leigh |
ObjectRepositoryFactory, using the createRepository method, passing an
|
| 28 |
|
james_leigh |
existing Repository. Once the ObjectRepository is created it is like
|
| 29 |
|
james_leigh |
other Sesame RDF Repositories, with full triple access, but it returns a
|
| 30 |
|
james_leigh |
ObjectConnection in the getConnection method. The ObjectConnection is an
|
| 31 |
|
james_leigh |
extension of the RepositoryConnection and includes additional methods
|
| 32 |
11450 |
james_leigh |
for working with objects and information-resources. However, before
|
| 33 |
|
james_leigh |
objects can be used, the object classes must first be created and registered.
|
| 34 |
9419 |
james_leigh |
|
| 35 |
11487 |
james_leigh |
To create classes for the ObjectRepository add the @Iri annotation to
|
| 36 |
9974 |
james_leigh |
all classes and fields (or interfaces and property methods)
|
| 37 |
|
james_leigh |
that should be stored in the repository. Then
|
| 38 |
9419 |
james_leigh |
create an empty 'META-INF/org.openrdf.concepts' file in the root
|
| 39 |
9434 |
james_leigh |
directory (or JAR) of the annotated classes. Once the
|
| 40 |
9421 |
james_leigh |
classes have been created, as shown in Figure 5,
|
| 41 |
9419 |
james_leigh |
they can be used with new ObjectRepositories.
|
| 42 |
|
james_leigh |
|
| 43 |
|
james_leigh |
<<Figure 5. A Class Compatible with the ObjectRepository>>
|
| 44 |
|
james_leigh |
|
| 45 |
|
james_leigh |
+--
|
| 46 |
|
james_leigh |
// Document.java
|
| 47 |
11486 |
james_leigh |
import org.openrdf.annotations.Iri;
|
| 48 |
9419 |
james_leigh |
|
| 49 |
11487 |
james_leigh |
@Iri(Document.NS + "Document")
|
| 50 |
9419 |
james_leigh |
public class Document {
|
| 51 |
|
james_leigh |
public static final String NS = "http://meta.leighnet.ca/rdf/2009/gs#";
|
| 52 |
9434 |
james_leigh |
|
| 53 |
11487 |
james_leigh |
@Iri(NS + "title") String title;
|
| 54 |
9434 |
james_leigh |
|
| 55 |
|
james_leigh |
public String getTitle() {
|
| 56 |
|
james_leigh |
return title;
|
| 57 |
|
james_leigh |
}
|
| 58 |
|
james_leigh |
public void setTitle(String title) {
|
| 59 |
|
james_leigh |
this.title = title;
|
| 60 |
|
james_leigh |
}
|
| 61 |
9419 |
james_leigh |
}
|
| 62 |
|
james_leigh |
+--
|
| 63 |
|
james_leigh |
|
| 64 |
|
james_leigh |
To add an object to the ObjectRepository, create an ObjectConnection and
|
| 65 |
|
james_leigh |
call the addObject method (as shown in Figure 6). This method will recursively add all other
|
| 66 |
|
james_leigh |
objects referenced from annotated fields. The addObject method can
|
| 67 |
|
james_leigh |
either automatically create a unique identifier for the object (that
|
| 68 |
|
james_leigh |
might change over time), or add the object using a provided identifier,
|
| 69 |
|
james_leigh |
called a URI. It is recommended to use a URI for any object that might
|
| 70 |
9973 |
james_leigh |
need to be referenced directly or has a conceptual identity, for all
|
| 71 |
10951 |
james_leigh |
other objects, such as anonymous collections, an automatic identifier
|
| 72 |
|
james_leigh |
may be good enough.
|
| 73 |
9419 |
james_leigh |
|
| 74 |
|
james_leigh |
To retrieve an existing object, use the getObject(Class, Resource)
|
| 75 |
|
james_leigh |
method of the ObjectConnection. The method accepts a URI or an anonymous
|
| 76 |
|
james_leigh |
identifier. An anonymous identifier maybe different for different
|
| 77 |
|
james_leigh |
ObjectConnections and should only be used within a single
|
| 78 |
9434 |
james_leigh |
ObjectConnection. A URI, however, will never change and can be used in any connection.
|
| 79 |
9419 |
james_leigh |
|
| 80 |
|
james_leigh |
Removing an object is more difficult, as every property of the object
|
| 81 |
|
james_leigh |
will need to be removed, by setting the fields or properties to null.
|
| 82 |
|
james_leigh |
Furthermore, the type of the object must also be removed from the
|
| 83 |
|
james_leigh |
repository, this can be done using the removeDesignation method of the
|
| 84 |
|
james_leigh |
ObjectConnection.
|
| 85 |
|
james_leigh |
|
| 86 |
|
james_leigh |
<<Figure 6. Using an ObjectConnection>>
|
| 87 |
|
james_leigh |
|
| 88 |
|
james_leigh |
+--
|
| 89 |
|
james_leigh |
// create a Document
|
| 90 |
|
james_leigh |
Document doc = new Document();
|
| 91 |
|
james_leigh |
doc.setTitle("Getting Started");
|
| 92 |
|
james_leigh |
|
| 93 |
|
james_leigh |
// add a Document to the repository
|
| 94 |
|
james_leigh |
ObjectConnection con = repository.getConnection();
|
| 95 |
|
james_leigh |
ValueFactory vf = con.getValueFactory();
|
| 96 |
|
james_leigh |
URI id = vf.createURI("http://meta.leighnet.ca/data/2009/getting-started");
|
| 97 |
9970 |
james_leigh |
con.addObject(id, doc);
|
| 98 |
9419 |
james_leigh |
|
| 99 |
|
james_leigh |
// retrieve a Document by id
|
| 100 |
|
james_leigh |
Document doc = con.getObject(Document.class, id);
|
| 101 |
|
james_leigh |
|
| 102 |
|
james_leigh |
// remove a Document from the repository
|
| 103 |
|
james_leigh |
Document doc = con.getObject(Document.class, id);
|
| 104 |
|
james_leigh |
doc.setTitle(null);
|
| 105 |
9970 |
james_leigh |
con.removeDesignation(doc, Document.class);
|
| 106 |
9434 |
james_leigh |
+--
|
| 107 |
|
james_leigh |
|
| 108 |
|
james_leigh |
Objects can also be retrieved by their type using the getObjects(Class)
|
| 109 |
|
james_leigh |
method, which includes subclasses. More fine grained queries can be
|
| 110 |
11496 |
james_leigh |
created using the @Sparql annotation. This annotation should be placed
|
| 111 |
|
james_leigh |
on public or protected methods that have a @Bind annotation on their parameters and have
|
| 112 |
9434 |
james_leigh |
a return type and parameters types of registered concepts or datatypes.
|
| 113 |
|
james_leigh |
The return type may also be a java.util.Set or Result of a concept or
|
| 114 |
9624 |
james_leigh |
datatype and may also be Model and any query result, such as GraphQueryResult,
|
| 115 |
10835 |
james_leigh |
TupleQueryResult, or boolean. Public and protected methods with this annotation will be
|
| 116 |
9434 |
james_leigh |
overridden with an optimized object query execution. The parameters with
|
| 117 |
11496 |
james_leigh |
an @Bind annotation will be available in the query in the variable name
|
| 118 |
9434 |
james_leigh |
provided. The target object is available in the query using the
|
| 119 |
|
james_leigh |
variables name "this".
|
| 120 |
9419 |
james_leigh |
|
| 121 |
9434 |
james_leigh |
Dynamic queries can be constructed using the prepareObjectQuery method
|
| 122 |
|
james_leigh |
or one of the other prepareQuery methods. The prepareObjectQuery method
|
| 123 |
|
james_leigh |
returns an ObjectQuery that allows objects and their type to be assigned to
|
| 124 |
|
james_leigh |
variables within the query before execution.
|
| 125 |
|
james_leigh |
|
| 126 |
|
james_leigh |
<<Figure 7. Executing Queries>>
|
| 127 |
|
james_leigh |
|
| 128 |
|
james_leigh |
+--
|
| 129 |
|
james_leigh |
|
| 130 |
9419 |
james_leigh |
// retrieve all Documents
|
| 131 |
|
james_leigh |
Result<Document> result = con.getObjects(Document.class);
|
| 132 |
|
james_leigh |
while (result.hasNext()) {
|
| 133 |
|
james_leigh |
out.println(result.next().getTitle());
|
| 134 |
|
james_leigh |
}
|
| 135 |
|
james_leigh |
|
| 136 |
11597 |
james_leigh |
import org.openrdf.annotations.Sparql;
|
| 137 |
|
james_leigh |
import org.openrdf.annotations.Bind;
|
| 138 |
|
james_leigh |
|
| 139 |
9596 |
james_leigh |
// retrieve a Document by title using a named query
|
| 140 |
11496 |
james_leigh |
@Sparql("PREFIX gs:<http://meta.leighnet.ca/rdf/2009/gs#>\n"+
|
| 141 |
9434 |
james_leigh |
"SELECT ?doc WHERE {?doc gs:title $title}")
|
| 142 |
11496 |
james_leigh |
public Document findDocumentByTitle(@Bind("title") String title) {
|
| 143 |
9434 |
james_leigh |
return null;
|
| 144 |
|
james_leigh |
}
|
| 145 |
|
james_leigh |
|
| 146 |
11450 |
james_leigh |
// retrieve a Document by title using a named query
|
| 147 |
|
james_leigh |
ValueFactory vf = con.getRepository().getValueFactory();
|
| 148 |
|
james_leigh |
URI myQueryID = vf.createURI("http://meta.leighnet.ca/rdf/2011/my-query");
|
| 149 |
|
james_leigh |
NamedQuery named = con.getRepository().createNamedQuery(myQueryID,
|
| 150 |
|
james_leigh |
"PREFIX gs:<http://meta.leighnet.ca/rdf/2009/gs#>\n"+
|
| 151 |
|
james_leigh |
"SELECT ?doc WHERE {?doc gs:title ?title}");
|
| 152 |
|
james_leigh |
|
| 153 |
|
james_leigh |
ObjectQuery query = con.prepareObjectQuery(named.getQueryString());
|
| 154 |
|
james_leigh |
query.setObject("title", "Getting Started");
|
| 155 |
11496 |
james_leigh |
Document doc = query.evaluate(Document.class).singleResult();
|
| 156 |
11450 |
james_leigh |
|
| 157 |
9434 |
james_leigh |
// retrieve a Document by title using a dynamic query
|
| 158 |
9419 |
james_leigh |
ObjectQuery query = con.prepareObjectQuery(
|
| 159 |
|
james_leigh |
"PREFIX gs:<http://meta.leighnet.ca/rdf/2009/gs#>\n"+
|
| 160 |
|
james_leigh |
"SELECT ?doc WHERE {?doc gs:title ?title}");
|
| 161 |
|
james_leigh |
query.setObject("title", "Getting Started");
|
| 162 |
11496 |
james_leigh |
Document doc = query.evaluate(Document.class).singleResult();
|
| 163 |
9419 |
james_leigh |
+--
|
| 164 |
9434 |
james_leigh |
|
| 165 |
10476 |
james_leigh |
Implementing Inverse Properties
|
| 166 |
|
james_leigh |
|
| 167 |
11496 |
james_leigh |
To simulate inverse properties in Java use named SPARQL queries for the Java getter and setter.
|
| 168 |
10951 |
james_leigh |
|
| 169 |
10476 |
james_leigh |
<<Figure 8. Inverse Property>>
|
| 170 |
|
james_leigh |
|
| 171 |
|
james_leigh |
+--
|
| 172 |
11487 |
james_leigh |
@Iri(FOAF + "Person")
|
| 173 |
10476 |
james_leigh |
public interface Person {
|
| 174 |
11487 |
james_leigh |
@Iri(FOAF + "depiction")
|
| 175 |
10476 |
james_leigh |
Image getDepiction();
|
| 176 |
|
james_leigh |
|
| 177 |
11487 |
james_leigh |
@Iri(FOAF + "depiction")
|
| 178 |
10476 |
james_leigh |
void setDepiction(Image depiction);
|
| 179 |
|
james_leigh |
}
|
| 180 |
|
james_leigh |
|
| 181 |
11487 |
james_leigh |
@Iri(FOAF + "Image")
|
| 182 |
10476 |
james_leigh |
public interface Image {
|
| 183 |
11496 |
james_leigh |
@Sparql(PREFIX + "SELECT ?person { ?person foaf:depiction $this }")
|
| 184 |
10476 |
james_leigh |
Person getDepicts();
|
| 185 |
|
james_leigh |
|
| 186 |
11496 |
james_leigh |
@Sparql(PREFIX + "DELETE { ?p foaf:depiction $this }\n"+
|
| 187 |
|
james_leigh |
"INSERT { $person foaf:depiction $this } WHERE {} ")
|
| 188 |
|
james_leigh |
void setDepicts(@Bind("person") Person person);
|
| 189 |
10476 |
james_leigh |
}
|
| 190 |
|
james_leigh |
+--
|
| 191 |
|
james_leigh |
|
| 192 |
11597 |
james_leigh |
Collections
|
| 193 |
|
james_leigh |
|
| 194 |
|
james_leigh |
In RDF the most natural collection is an unordered set, or non-functional
|
| 195 |
|
james_leigh |
property. Sets are triples that share the same subject and predicate.
|
| 196 |
|
james_leigh |
these non-functional properties should have a java.util.Set property type in
|
| 197 |
|
james_leigh |
Java.
|
| 198 |
|
james_leigh |
|
| 199 |
|
james_leigh |
Ordered collections in RDF include rdf:List, rdfs:Container, rdf:Seq, rdf:Alt,
|
| 200 |
|
james_leigh |
and rdf:Bag. AliBaba provides a java.util.List interface for each of these
|
| 201 |
|
james_leigh |
resource types. However, all ordered collections in the RDF store must include
|
| 202 |
|
james_leigh |
an rdf:type on the root node. Often RDF formats that include syntax sugar for
|
| 203 |
|
james_leigh |
rdf:List do not include a rdf:type and may not be readable in AliBaba. To add
|
| 204 |
|
james_leigh |
the missing add the triple rdf:type rdf:List using the add statement method of
|
| 205 |
|
james_leigh |
the ObjectConnection. Other Java collections that implement java.util.List are
|
| 206 |
|
james_leigh |
mapped to rdfs:Container when merged into the store.
|
| 207 |
|
james_leigh |
|
| 208 |
|
james_leigh |
Most RDF stores (like SQL databases) are not optimized for generic ordered
|
| 209 |
|
james_leigh |
collections. Developers will find unordered collections have significantly
|
| 210 |
|
james_leigh |
reduce I/O and better performance. If the elements of an ordered collection
|
| 211 |
|
james_leigh |
will only exist in (at most) one ordered collection, it is recommended instead
|
| 212 |
|
james_leigh |
to use a typed unordered collection (functional property) and included a
|
| 213 |
|
james_leigh |
functional index member property on the elements. The elements can then be
|
| 214 |
|
james_leigh |
sorted in memory when necessary.
|
| 215 |
|
james_leigh |
|
| 216 |
|
james_leigh |
Figure 9 show an example of using an unordered collection with an explicit
|
| 217 |
|
james_leigh |
result order. The method getOrderedChildren() will order the nodes in the RDF
|
| 218 |
|
james_leigh |
store (often in memory), while the method getSortedChildren() will sort them in
|
| 219 |
|
james_leigh |
Java. In both cases calling java.util.List#add(Object) has no effect on the RDF
|
| 220 |
|
james_leigh |
store.
|
| 221 |
|
james_leigh |
|
| 222 |
|
james_leigh |
<<Figure 9. Unordered Collection with element index>>
|
| 223 |
|
james_leigh |
|
| 224 |
|
james_leigh |
+--
|
| 225 |
|
james_leigh |
import org.openrdf.annotations.Iri;
|
| 226 |
|
james_leigh |
|
| 227 |
|
james_leigh |
@Iri(NS + "Node")
|
| 228 |
|
james_leigh |
public interface Node {
|
| 229 |
|
james_leigh |
@Iri(NS + "child")
|
| 230 |
|
james_leigh |
Set<Node> getChildren();
|
| 231 |
|
james_leigh |
|
| 232 |
|
james_leigh |
@Iri(NS + "child")
|
| 233 |
|
james_leigh |
void setChildren(Set<Node> children);
|
| 234 |
|
james_leigh |
|
| 235 |
|
james_leigh |
@Iri(NS + "position")
|
| 236 |
|
james_leigh |
Integer getPosition();
|
| 237 |
|
james_leigh |
|
| 238 |
|
james_leigh |
@Iri(NS + "position")
|
| 239 |
|
james_leigh |
void setPosition(Integer position);
|
| 240 |
|
james_leigh |
|
| 241 |
|
james_leigh |
@Sparql(PREFIX
|
| 242 |
|
james_leigh |
+ "SELECT ?child { $this ex:child ?child . ?child ex:position ?position }\n"
|
| 243 |
|
james_leigh |
+ "ORDER BY ?position")
|
| 244 |
|
james_leigh |
List<Node> getOrderedChildren();
|
| 245 |
|
james_leigh |
|
| 246 |
|
james_leigh |
List<Node> getSortedChildren();
|
| 247 |
|
james_leigh |
}
|
| 248 |
|
james_leigh |
|
| 249 |
|
james_leigh |
public abstract class NodeSupport implements Node {
|
| 250 |
|
james_leigh |
public List<Node> getSortedChildren() {
|
| 251 |
|
james_leigh |
Set<Node> live = getChildren();
|
| 252 |
|
james_leigh |
List<Node> memory = new ArrayList<Node>(live);
|
| 253 |
|
james_leigh |
Collections.sort(memory, new Comparator<Node>() {
|
| 254 |
|
james_leigh |
public int compare(Node o1, Node o2) {
|
| 255 |
|
james_leigh |
Integer p1 = o1.getPosition();
|
| 256 |
|
james_leigh |
Integer p2 = o2.getPosition();
|
| 257 |
|
james_leigh |
if (p1 == p2)
|
| 258 |
|
james_leigh |
return 0;
|
| 259 |
|
james_leigh |
if (p1 == null)
|
| 260 |
|
james_leigh |
return -1;
|
| 261 |
|
james_leigh |
if (p2 == null)
|
| 262 |
|
james_leigh |
return 1;
|
| 263 |
|
james_leigh |
return p1.compareTo(p2);
|
| 264 |
|
james_leigh |
}
|
| 265 |
|
james_leigh |
});
|
| 266 |
|
james_leigh |
return memory;
|
| 267 |
|
james_leigh |
}
|
| 268 |
|
james_leigh |
}
|
| 269 |
|
james_leigh |
+--
|
| 270 |
|
james_leigh |
|
| 271 |
10951 |
james_leigh |
Aspect-Oriented Programming
|
| 272 |
|
james_leigh |
|
| 273 |
|
james_leigh |
AliBaba allows any method call (including getters and setters) to be used as
|
| 274 |
11597 |
james_leigh |
join-points for cross cutting concerns. Figure 11 show the concept interface
|
| 275 |
|
james_leigh |
Person (with an @Iri annotation) which defines a join-point
|
| 276 |
|
james_leigh |
setDepiction(Image):void and the abstract behaviour class PersonSupport
|
| 277 |
10951 |
james_leigh |
(implements a concept interface) defines an aspect for that join-point by using
|
| 278 |
|
james_leigh |
the same method name, parameter types, and return type. Other classes may
|
| 279 |
|
james_leigh |
implement their own aspects using the same join-point and would be executed
|
| 280 |
|
james_leigh |
serially until all are executed or a non-null (nor 0 nor false) response is given.
|
| 281 |
|
james_leigh |
|
| 282 |
|
james_leigh |
For more control over the execution order of aspects, AliBaba
|
| 283 |
11496 |
james_leigh |
provides the annotation @Precedes, which can be placed on a behaviour class
|
| 284 |
10951 |
james_leigh |
with a list of other behaviour classes, who's aspects should not be executed
|
| 285 |
|
james_leigh |
before the aspects of this annotated behaviour class. Aspects can also intercept
|
| 286 |
11496 |
james_leigh |
method executions by using the annotation @ParameterTypes when declaring the
|
| 287 |
10951 |
james_leigh |
aspect. The annotation should list the parameter types of the join-point, while
|
| 288 |
|
james_leigh |
the aspect parameter type is one of ObjectMessage, BooleanMessage, ByteMessage,
|
| 289 |
|
james_leigh |
CharacterMessage, DoubleMessage, FloatMessage, IntegerMessage, LongMessage,
|
| 290 |
|
james_leigh |
ShortMessage, and VoidMessage corresponding to the object or primitive return type of the
|
| 291 |
|
james_leigh |
aspect/join-point. When the aspect is executed the parameters of the method
|
| 292 |
|
james_leigh |
call and the return type (thus far) are available through one of the previously
|
| 293 |
11597 |
james_leigh |
listed message interfaces. Figure 11 shows an example of an aspect, which
|
| 294 |
10951 |
james_leigh |
conditionally changes the response of a method call to ensure it is never null.
|
| 295 |
|
james_leigh |
|
| 296 |
11597 |
james_leigh |
<<Figure 11. Intercept method call>>
|
| 297 |
10951 |
james_leigh |
|
| 298 |
|
james_leigh |
+--
|
| 299 |
11487 |
james_leigh |
@Iri(FOAF + "Person")
|
| 300 |
10951 |
james_leigh |
public interface Person {
|
| 301 |
11487 |
james_leigh |
@Iri(FOAF + "depiction")
|
| 302 |
10951 |
james_leigh |
Image getDepiction();
|
| 303 |
|
james_leigh |
|
| 304 |
11487 |
james_leigh |
@Iri(FOAF + "depiction")
|
| 305 |
10951 |
james_leigh |
void setDepiction(Image depiction);
|
| 306 |
|
james_leigh |
}
|
| 307 |
|
james_leigh |
|
| 308 |
|
james_leigh |
public abstract class PersonSupport implements Person, RDFObject {
|
| 309 |
11496 |
james_leigh |
@ParameterTypes({})
|
| 310 |
|
james_leigh |
public Image getDepiction(ObjectMessage msg) throws RepositoryException {
|
| 311 |
10951 |
james_leigh |
Image depiction = (Image) msg.proceed();
|
| 312 |
|
james_leigh |
if (depiction == null) {
|
| 313 |
|
james_leigh |
return (Image) getObjectConnection().getObject(DEFAULT_IMAGE_URI);
|
| 314 |
|
james_leigh |
}
|
| 315 |
|
james_leigh |
return depiction;
|
| 316 |
|
james_leigh |
}
|
| 317 |
11597 |
james_leigh |
|
| 318 |
|
james_leigh |
void setDepiction(Image depiction) {
|
| 319 |
|
james_leigh |
// With @ParameterTypes is not used call automatically proceeds
|
| 320 |
|
james_leigh |
System.out.println("setDepiction called with: " + depiction);
|
| 321 |
|
james_leigh |
}
|
| 322 |
10951 |
james_leigh |
}
|
| 323 |
|
james_leigh |
+--
|
| 324 |
|
james_leigh |
|
| 325 |
11450 |
james_leigh |
Information Resources (BLOBs)
|
| 326 |
|
james_leigh |
|
| 327 |
11597 |
james_leigh |
The method getBlobObject can be used to retrive an FileObject interface of an
|
| 328 |
11450 |
james_leigh |
information resource by URI. The FileObject interface includes methods to open
|
| 329 |
|
james_leigh |
an InputStream and an OutputStream and will be stored by URI to the configured
|
| 330 |
|
james_leigh |
directory. Changes to the blobs will be isolated from other connections until
|
| 331 |
|
james_leigh |
the changes are committed (if not in autoCommit mode).
|
| 332 |
|
james_leigh |
|
| 333 |
|
james_leigh |
The delegate BlobStore can be set using the setBlobStore method of the
|
| 334 |
|
james_leigh |
ObjectRepository before use. BlobStores are created using the BlobStoreFactory
|
| 335 |
|
james_leigh |
openBlobStore(File) method.
|
| 336 |
|
james_leigh |
|
| 337 |
9434 |
james_leigh |
Generating Concepts
|
| 338 |
9419 |
james_leigh |
|
| 339 |
|
james_leigh |
Compatible class files can be created from RDFS/OWL files, for use with
|
| 340 |
|
james_leigh |
the ObjectRepository in Java, by using the provided owl-compiler.sh (or
|
| 341 |
|
james_leigh |
.bat) file, with main class
|
| 342 |
|
james_leigh |
org.openrdf.repository.object.compiler.Compiler. Use the '-h' option to
|
| 343 |
|
james_leigh |
review the available command line options.
|
| 344 |
|
james_leigh |
|
| 345 |
|
james_leigh |
When precompiled class files are not needed in advance, the
|
| 346 |
|
james_leigh |
ObjectRepository can compile them itself. When the AliBaba JARs are
|
| 347 |
|
james_leigh |
added to the console, additional repository templates are included to
|
| 348 |
|
james_leigh |
facilitate creating the ObjectRepository. These include object-memory
|
| 349 |
|
james_leigh |
and object-native (among others). When creating the repository with the
|
| 350 |
|
james_leigh |
console, it will prompt for an OWL Ontology file that should contain the
|
| 351 |
|
james_leigh |
classes and properties needed and/or reference them using owl:imports
|
| 352 |
|
james_leigh |
statements within the file.
|
| 353 |
|
james_leigh |
|
| 354 |
11597 |
james_leigh |
<<Figure 12. Creating ObjectRepository from the Console>>
|
| 355 |
9419 |
james_leigh |
|
| 356 |
|
james_leigh |
+--
|
| 357 |
|
james_leigh |
Commands end with '.' at the end of a line
|
| 358 |
|
james_leigh |
Type 'help.' for help
|
| 359 |
9590 |
james_leigh |
> connect data.
|
| 360 |
9419 |
james_leigh |
Disconnecting from default data directory
|
| 361 |
9590 |
james_leigh |
Connected to data
|
| 362 |
9419 |
james_leigh |
> create object-native.
|
| 363 |
|
james_leigh |
Please specify values for the following variables:
|
| 364 |
|
james_leigh |
Repository ID [native]: foaf
|
| 365 |
|
james_leigh |
Repository title [Native store]: FOAF Store
|
| 366 |
10691 |
james_leigh |
Rollback if multiple states observed (enforce snapshot)? (false|true) [false]:
|
| 367 |
|
james_leigh |
Rollback if outdated state observed (enforce serializable)? (false|true) [false]:
|
| 368 |
|
james_leigh |
Changeset namespace [urn:trx:localhost:]:
|
| 369 |
10840 |
james_leigh |
Archive all removed data (false|true) [false]:
|
| 370 |
|
james_leigh |
If not, archive transactions with removed triples less than [100]:
|
| 371 |
10691 |
james_leigh |
Minimum recent transactions [100]:
|
| 372 |
|
james_leigh |
Maximum recent transactions [1000]:
|
| 373 |
|
james_leigh |
Triple indexes [spoc,posc]:
|
| 374 |
|
james_leigh |
Max Query Time [0]:
|
| 375 |
|
james_leigh |
Default Query Language [SPARQL]:
|
| 376 |
9419 |
james_leigh |
Ontology [http://www.w3.org/2002/07/owl]: http://xmlns.com/foaf/spec/index.rdf
|
| 377 |
9596 |
james_leigh |
Read Schema from Repository [false]:
|
| 378 |
9419 |
james_leigh |
Repository created
|
| 379 |
10691 |
james_leigh |
|
| 380 |
9419 |
james_leigh |
> quit.
|
| 381 |
9590 |
james_leigh |
Disconnecting from data
|
| 382 |
9419 |
james_leigh |
Bye
|
| 383 |
|
james_leigh |
+--
|
| 384 |
|
james_leigh |
|
| 385 |
|
james_leigh |
Scripts can be streamlined by allowing the ObjectRepository to compile
|
| 386 |
11597 |
james_leigh |
the ontology. Shown in Figure 13 is jrunscript (in
|
| 387 |
9419 |
james_leigh |
JavaScript) that outputs a new FOAF file, demonstrating how RDF/Objects
|
| 388 |
|
james_leigh |
can be used without compiling Java files.
|
| 389 |
|
james_leigh |
|
| 390 |
11597 |
james_leigh |
<<Figure 13. JRunScript and ObjectRepository>>
|
| 391 |
9419 |
james_leigh |
|
| 392 |
|
james_leigh |
+--
|
| 393 |
9974 |
james_leigh |
$ jrunscript -J-Djava.ext.dirs=lib:dist
|
| 394 |
9590 |
james_leigh |
js> var rm = org.openrdf.repository.manager.RepositoryProvider.getRepositoryManager("data")
|
| 395 |
9419 |
james_leigh |
js> var repo = rm.getRepository("foaf")
|
| 396 |
|
james_leigh |
js> var con = repo.getConnection()
|
| 397 |
10691 |
james_leigh |
js> con.setAutoCommit(false)
|
| 398 |
|
james_leigh |
js> var Person = "http://xmlns.com/foaf/0.1/Person"
|
| 399 |
|
james_leigh |
js> var base = "http://example.com/person/"
|
| 400 |
|
james_leigh |
js> var james = con.addDesignation(con.getObject(base+"james"), Person)
|
| 401 |
9977 |
james_leigh |
js>
|
| 402 |
11496 |
james_leigh |
js> james.foafFirstName.add("James")
|
| 403 |
|
james_leigh |
js> james.foafSurname.add("Leigh")
|
| 404 |
|
james_leigh |
js> james.foafInterest.add("RDF")
|
| 405 |
10691 |
james_leigh |
js> var arjohn = con.addDesignation(con.getObject(base+"arjohn"), Person)
|
| 406 |
11496 |
james_leigh |
js> arjohn.foafFirstName.add("Arjohn")
|
| 407 |
9434 |
james_leigh |
js> james.foafKnows.add(arjohn)
|
| 408 |
9977 |
james_leigh |
js>
|
| 409 |
10691 |
james_leigh |
js> con.setNamespace("foaf", "http://xmlns.com/foaf/0.1/")
|
| 410 |
9419 |
james_leigh |
js> con['export'](new org.openrdf.rio.rdfxml.RDFXMLWriter(java.lang.System.out), [])
|
| 411 |
|
james_leigh |
<?xml version="1.0" encoding="UTF-8"?>
|
| 412 |
|
james_leigh |
<rdf:RDF
|
| 413 |
|
james_leigh |
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
| 414 |
|
james_leigh |
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
| 415 |
|
james_leigh |
|
| 416 |
9434 |
james_leigh |
<rdf:Description rdf:about="http://meta.leighnet.ca/data/rdf/2009/foaf/james">
|
| 417 |
9419 |
james_leigh |
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
| 418 |
|
james_leigh |
<foaf:firstName>James</foaf:firstName>
|
| 419 |
|
james_leigh |
<foaf:surname>Leigh</foaf:surname>
|
| 420 |
|
james_leigh |
<foaf:interest>RDF</foaf:interest>
|
| 421 |
|
james_leigh |
<foaf:knows rdf:resource="http://meta.leighnet.ca/data/rdf/2009/foaf/arjohn"/>
|
| 422 |
|
james_leigh |
</rdf:Description>
|
| 423 |
|
james_leigh |
|
| 424 |
|
james_leigh |
<rdf:Description rdf:about="http://meta.leighnet.ca/data/rdf/2009/foaf/arjohn">
|
| 425 |
|
james_leigh |
<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
|
| 426 |
|
james_leigh |
<foaf:firstName>Arjohn</foaf:firstName>
|
| 427 |
|
james_leigh |
</rdf:Description>
|
| 428 |
|
james_leigh |
|
| 429 |
|
james_leigh |
</rdf:RDF>
|
| 430 |
|
james_leigh |
js> con.close()
|
| 431 |
|
james_leigh |
+--
|
| 432 |
9973 |
james_leigh |
|
| 433 |
9974 |
james_leigh |
Message Vocabulary
|
| 434 |
9973 |
james_leigh |
|
| 435 |
10951 |
james_leigh |
In addition to the RDFS and OWL vocabulary, the object repository also supports
|
| 436 |
|
james_leigh |
its own vocabulary for describing messages. This vocabulary can be used to
|
| 437 |
11597 |
james_leigh |
declare interface methods and implementations. These messages
|
| 438 |
10951 |
james_leigh |
can be created by extending the class msg:Message and creating restrictions for
|
| 439 |
|
james_leigh |
its msg:target and response properties (msg:object msg:objectSet msg:literal
|
| 440 |
|
james_leigh |
msg:literalSet). Implementations can be created in ECMA script, sparql, or
|
| 441 |
|
james_leigh |
xslt. Parameters (message properties) and the variable "this" is available in
|
| 442 |
|
james_leigh |
the implementations as (prefix + initcap local part) of the property URI or
|
| 443 |
|
james_leigh |
just the local part if no prefix is defined. msg:script code also has the
|
| 444 |
|
james_leigh |
method proceed() available to execute other implementations of the message. The
|
| 445 |
|
james_leigh |
last parameter (alpha sorted) is the body input of an msg:xslt message. Show in
|
| 446 |
11597 |
james_leigh |
Figure 14 is a sample of what a message might look like in turtle.
|
| 447 |
9974 |
james_leigh |
|
| 448 |
11597 |
james_leigh |
<<Figure 14. Sample Usage of Object Vocabulary>>
|
| 449 |
9973 |
james_leigh |
|
| 450 |
9974 |
james_leigh |
+--
|
| 451 |
|
james_leigh |
@prefix xsd:<http://www.w3.org/2001/XMLSchema#>.
|
| 452 |
|
james_leigh |
@prefix rdfs:<http://www.w3.org/2000/01/rdf-schema#>.
|
| 453 |
|
james_leigh |
@prefix owl:<http://www.w3.org/2002/07/owl#>.
|
| 454 |
10835 |
james_leigh |
@prefix msg:<http://www.openrdf.org/rdf/2011/messaging#>.
|
| 455 |
9974 |
james_leigh |
@prefix :<http://data.leighnet.ca/rdf/2009/example#>.
|
| 456 |
9973 |
james_leigh |
|
| 457 |
9974 |
james_leigh |
# Declare classes and properties for this example
|
| 458 |
|
james_leigh |
:Mammal rdfs:subClassOf rdfs:Resource.
|
| 459 |
|
james_leigh |
:Person rdfs:subClassOf :Mammal.
|
| 460 |
|
james_leigh |
:Dog rdfs:subClassOf :Mammal.
|
| 461 |
|
james_leigh |
|
| 462 |
|
james_leigh |
:dateOfBirth a owl:DatatypeProperty; a owl:FunctionalProperty;
|
| 463 |
|
james_leigh |
rdfs:domain :Mammal;
|
| 464 |
|
james_leigh |
rdfs:range xsd:date.
|
| 465 |
|
james_leigh |
|
| 466 |
9977 |
james_leigh |
# Common message that responds with the current date time
|
| 467 |
10835 |
james_leigh |
:GetCurrentTime rdfs:subClassOf msg:Message;
|
| 468 |
|
james_leigh |
rdfs:subClassOf [owl:onProperty msg:literal; owl:allValuesFrom xsd:dateTime];
|
| 469 |
|
james_leigh |
msg:imports <java:javax.xml.datatype.DatatypeFactory>;
|
| 470 |
|
james_leigh |
msg:imports <java:java.util.GregorianCalendar>;
|
| 471 |
|
james_leigh |
msg:script """
|
| 472 |
10541 |
james_leigh |
var df = DatatypeFactory.newInstance();
|
| 473 |
9977 |
james_leigh |
return df.newXMLGregorianCalendar(new GregorianCalendar());
|
| 474 |
9974 |
james_leigh |
""".
|
| 475 |
|
james_leigh |
|
| 476 |
9977 |
james_leigh |
# Common message that take a date time and responds with the duration since then
|
| 477 |
10835 |
james_leigh |
# This message uses the parameter when and the previous message to compute a new value
|
| 478 |
|
james_leigh |
:GetDurationSince rdfs:subClassOf msg:Message;
|
| 479 |
|
james_leigh |
rdfs:subClassOf [owl:onProperty msg:literal; owl:allValuesFrom xsd:duration];
|
| 480 |
|
james_leigh |
msg:imports <java:javax.xml.datatype.DatatypeFactory>;
|
| 481 |
|
james_leigh |
msg:script """
|
| 482 |
10541 |
james_leigh |
var df = DatatypeFactory.newInstance();
|
| 483 |
10835 |
james_leigh |
var now = this.GetCurrentTime().toGregorianCalendar().getTimeInMillis();
|
| 484 |
10691 |
james_leigh |
var since = when.toGregorianCalendar().getTimeInMillis();
|
| 485 |
|
james_leigh |
return df.newDuration(now - since);
|
| 486 |
9974 |
james_leigh |
""".
|
| 487 |
|
james_leigh |
|
| 488 |
10691 |
james_leigh |
:when a owl:DatatypeProperty; a owl:FunctionalProperty;
|
| 489 |
|
james_leigh |
rdfs:domain :GetDurationSince;
|
| 490 |
9974 |
james_leigh |
rdfs:range xsd:dateTime.
|
| 491 |
|
james_leigh |
|
| 492 |
|
james_leigh |
# Message for the age of a Mammal
|
| 493 |
10541 |
james_leigh |
# This message is written in ECMA script and uses a compact syntax
|
| 494 |
10835 |
james_leigh |
:GetCurrentAge rdfs:subClassOf msg:Message;
|
| 495 |
|
james_leigh |
rdfs:subClassOf [owl:onProperty msg:target; owl:allValuesFrom :Mammal];
|
| 496 |
|
james_leigh |
rdfs:subClassOf [owl:onProperty msg:literal; owl:allValuesFrom xsd:int];
|
| 497 |
|
james_leigh |
msg:script "return this.GetDurationSince(this.dateOfBirth).years".
|
| 498 |
9974 |
james_leigh |
|
| 499 |
|
james_leigh |
# Dog's age is calculated differently than other mammals
|
| 500 |
9977 |
james_leigh |
# This message is a specialisation of the previous and overrides it for all dogs
|
| 501 |
10541 |
james_leigh |
# The proceed() method is used to call and return the response from intercepted methods
|
| 502 |
10835 |
james_leigh |
:GetCurrentDogAge owl:intersectionOf (:GetCurrentAge [owl:onProperty msg:target; owl:allValuesFrom :Dog]);
|
| 503 |
10840 |
james_leigh |
msg:script "return proceed() * 7".
|
| 504 |
9974 |
james_leigh |
|
| 505 |
|
james_leigh |
# Intercepts the age message and checks if the mammal is less then a year old
|
| 506 |
10691 |
james_leigh |
:GetCurrentAgeInMonths owl:equivalentClass :GetCurrentAge;
|
| 507 |
10835 |
james_leigh |
msg:script """
|
| 508 |
10541 |
james_leigh |
var year = proceed();
|
| 509 |
9974 |
james_leigh |
if (year < 2) {
|
| 510 |
10835 |
james_leigh |
var duration = this.GetDurationSince(this.dateOfBirth);
|
| 511 |
10691 |
james_leigh |
return duration.years * 12 + duration.months;
|
| 512 |
9974 |
james_leigh |
}
|
| 513 |
|
james_leigh |
return year;
|
| 514 |
|
james_leigh |
""".
|
| 515 |
|
james_leigh |
|
| 516 |
|
james_leigh |
# Some sample data to test with
|
| 517 |
|
james_leigh |
:jack a :Dog;
|
| 518 |
|
james_leigh |
:dateOfBirth "2005-02-18"^^xsd:date.
|
| 519 |
|
james_leigh |
|
| 520 |
|
james_leigh |
:mel a :Person;
|
| 521 |
|
james_leigh |
:dateOfBirth "1956-01-03"^^xsd:date.
|
| 522 |
|
james_leigh |
|
| 523 |
|
james_leigh |
:lucia a :Person;
|
| 524 |
|
james_leigh |
:dateOfBirth "2009-10-30"^^xsd:date.
|
| 525 |
|
james_leigh |
+--
|
| 526 |
|
james_leigh |
|
| 527 |
11597 |
james_leigh |
When an object repository is set to use the Ontology in Figure 14, the connection in Figure 15 can be used to evaluate the messages and calulate the age. Since an empty prefix was used in the ontology, no prefix is used when calling messages (or properties). If the schema changes at runtime the method ObjectConnection#recompileSchemaOnClose() should be called to compile the changes within the ObjectConnection#close() method.
|
| 528 |
9974 |
james_leigh |
|
| 529 |
11597 |
james_leigh |
<<Figure 15. Creating ObjectRepository from the Console>>
|
| 530 |
9974 |
james_leigh |
|
| 531 |
|
james_leigh |
+--
|
| 532 |
10840 |
james_leigh |
Commands end with '.' at the end of a line
|
| 533 |
|
james_leigh |
Type 'help.' for help
|
| 534 |
|
james_leigh |
> connect data.
|
| 535 |
|
james_leigh |
Disconnecting from default data directory
|
| 536 |
|
james_leigh |
Connected to data
|
| 537 |
|
james_leigh |
> create object-native.
|
| 538 |
|
james_leigh |
Please specify values for the following variables:
|
| 539 |
|
james_leigh |
Repository ID [native]: mammals
|
| 540 |
|
james_leigh |
Repository title [Native store]: Mammal Store
|
| 541 |
|
james_leigh |
Rollback if multiple states observed (enforce snapshot)? (false|true) [false]:
|
| 542 |
|
james_leigh |
Rollback if outdated state observed (enforce serializable)? (false|true) [false]:
|
| 543 |
|
james_leigh |
Changeset namespace [urn:trx:localhost:]:
|
| 544 |
|
james_leigh |
Archive all removed data (false|true) [false]:
|
| 545 |
|
james_leigh |
If not, archive transactions with removed triples less than [100]:
|
| 546 |
|
james_leigh |
Minimum recent transactions [100]:
|
| 547 |
|
james_leigh |
Maximum recent transactions [1000]:
|
| 548 |
|
james_leigh |
Triple indexes [spoc,posc]:
|
| 549 |
|
james_leigh |
Max Query Time [0]:
|
| 550 |
|
james_leigh |
Default Query Language [SPARQL]:
|
| 551 |
|
james_leigh |
Ontology [http://www.w3.org/2002/07/owl]:
|
| 552 |
|
james_leigh |
Read Schema from Repository [false]: true
|
| 553 |
|
james_leigh |
Repository created
|
| 554 |
|
james_leigh |
|
| 555 |
|
james_leigh |
> open mammals.
|
| 556 |
|
james_leigh |
Opened repository 'mammals'
|
| 557 |
|
james_leigh |
mammals> load mammals.ttl.
|
| 558 |
|
james_leigh |
Loading data...
|
| 559 |
|
james_leigh |
Data has been added to the repository (3048 ms)
|
| 560 |
|
james_leigh |
mammals> quit.
|
| 561 |
|
james_leigh |
Closing repository 'mammals'...
|
| 562 |
|
james_leigh |
Disconnecting from data
|
| 563 |
|
james_leigh |
Bye
|
| 564 |
|
james_leigh |
+--
|
| 565 |
|
james_leigh |
|
| 566 |
11597 |
james_leigh |
<<Figure 16. Calling Object Messages from JavaScript>>
|
| 567 |
10840 |
james_leigh |
|
| 568 |
|
james_leigh |
+--
|
| 569 |
|
james_leigh |
$ jrunscript -J-Djava.ext.dirs=lib:dist
|
| 570 |
|
james_leigh |
js> var rm = org.openrdf.repository.manager.RepositoryProvider.getRepositoryManager("data")
|
| 571 |
|
james_leigh |
js> var repo = rm.getRepository("mammals")
|
| 572 |
9974 |
james_leigh |
js> var con = repo.getConnection()
|
| 573 |
|
james_leigh |
js> var jack = con.getObject("http://data.leighnet.ca/rdf/2009/example#jack")
|
| 574 |
|
james_leigh |
js> var mel = con.getObject("http://data.leighnet.ca/rdf/2009/example#mel")
|
| 575 |
|
james_leigh |
js> var lucia = con.getObject("http://data.leighnet.ca/rdf/2009/example#lucia")
|
| 576 |
10691 |
james_leigh |
js> jack.GetCurrentAge()
|
| 577 |
|
james_leigh |
35
|
| 578 |
|
james_leigh |
js> mel.GetCurrentAge()
|
| 579 |
|
james_leigh |
54
|
| 580 |
|
james_leigh |
js> lucia.GetCurrentAge()
|
| 581 |
|
james_leigh |
13
|
| 582 |
9974 |
james_leigh |
js> con.close()
|
| 583 |
|
james_leigh |
+--
|
| 584 |
|
james_leigh |
|
| 585 |
9419 |
james_leigh |
The ObjectRepository simplifies interacting with RDF resources in OO
|
| 586 |
|
james_leigh |
languages on the JVM. By bridging RDF properties and object properties,
|
| 587 |
|
james_leigh |
creating and manipulating RDF resources is as easy as manipulating objects.
|
| 588 |
|
james_leigh |
|
| 589 |
9486 |
james_leigh |
|