JenaRules
From Wiki
Contents |
Introduction
- A Java-based framework for building Semantic Web applications.
- An environment for RDF, RDFS and OWL, SPARQL and includes a rule-based inference engine.
- Three kind of rules (according with their execution model): forward rules, backward rules and hybrid rules.
- Java-based reasoner implementation. An API description is available.
Creating the Authoring Environment
- In order to execute your own rules and queries, the Jena API must be downloaded.
- Add this library to
CLASSPATHenvironment variable. This is done usually by editing your environment variable and adding the path to your directory where the you have the jar file containing the Jena API. - At this step, you can use a simple text editor, or a more sophisticated IDE such as Eclipse to build your own Jena applications.
Writing Rules in Jena
First Example
See also: Jena Rules Examples
The Syntax of Jena Rules
JenaRules is based on RDF(S) and uses the triple representation of RDF descriptions (see also N3 Notation and Turtle Syntax). The abstract syntax of JenaRules is:
Rule := bare-rule .
or [ bare-rule ]
or [ ruleName : bare-rule ]
bare-rule := term, ... term -> hterm, ... hterm // forward rule
or term, ... term <- term, ... term // backward rule
hterm := term
or [ bare-rule ]
term := (node, node, node) // triple pattern
or (node, node, functor) // extended triple pattern
or builtin(node, ... node) // invoke procedural primitive
functor := functorName(node, ... node) // structured literal
node := uri-ref // e.g. http://foo.com/eg
or prefix:localname // e.g. rdf:type
or <uri-ref> // e.g. <myscheme:myuri>
or ?varname // variable
or 'a literal' // a plain string literal
or 'lex'^^typeURI // a typed literal, xs:* type names supported
or number // e.g. 42 or 25.5
Terms
Jena Rules terms are: URI References, variables, blank nodes, plain literals and typed literals: Also, JenaRules allows Qualified Names to be used as URI References. They have to be solved by the engine to complete URI's. Variables - are valid identifiers prefixed by the ? symbol (i.e. ?s or ?student). The syntax for blank nodes is _:varName (i.e. _:s). Plain literals are string literals enclosed in " or ' (i.e. "John" or 'John'). Typed literals are literals for which we know the type (i.e. integers, double, date). The required syntax use the syntax for plain literals and ^^ to specifies the type (i.e. "40"^^xs:integer). XML Schema datatypes are supported.
See also: N3 Notation
Atoms
We can identify two types of atoms in a Jena rule:
- triple patterns - structures of type (subject predicate object), where subject, predicate and object are nodes: i.e. (ex:John rdf:type ex:DiligentStudent) or (?s ex:passedExam ?y).
See also: N3 Notation and Turtle Syntax
- built-ins - boolean predicates with zero or many arguments: i.e. notEqual(?x, ?y) or noValue(ex:John ex:passedExam ex:SemanticWeb). Built-is comes in a predefined set but the user can add it's own built-ins using the API.
See Also: SWRL Builtins
Rules
JenaRules provides three mechanism of rule execution:
- backward chaining rules - are identified by using '<-'. These rules are interpreted as queries and the rule head (the conclusion part) contains a single atom which cannot be a built-in.
The body (the condition part) is a conjunction between one or more triple patterns and/or built-ins. A simple backward chaining rule is depicted in Example 1.
- forward chaining rules - are identified by using '->'. They are executed by a forward engine and both the head and body may contain of conjunction of atoms. See the Example 2.
- hybrid rules consists in using one or more backward chaining rules in the head of a forward chaining rule. Both rules composing the hybrid rule follows the syntax and restrictions presented above. A simple example of a hybrid rule is depicted in Example 3. This is interpreted as, the actions of the production rules are the results of the derivations rules, if there are such results. This are only insertions of new facts in the working memory.
Before we can use a qualified name, the corresponding namespace must be defined. This is done by using the @prefix directive.
Example 1 ( Backward Chaining Rule)
A student is diligent if it has at least 60 credit points.
@prefix ex: http://example.com/school#
[diligent: (?s rdf:type ex:DiligentStudent)
<-
(?s rdf:type ex:Student)
(?s ex:totalCredits ?c)
greaterThan(?c,60)
]
The above rule name, in diligent. Different rules can have the same name. Every rule is enclosed in [] symbols. This is mandatory, since it is used in order to extract every rule from a file containing many rules.
Example 2 (Forward chaining rule)
If a student obtains at least 4 points to the written exam, then its final grade is the sum of the points from the written exam and the points obtained at the practical exam.
@prefix ex: http://example.com/school#
[totalPoints: (?s rdf:type ex:Student)
(?s ex:writtenExamPoints ?pw)
(?s ex:labExamPoints ?lp)
ge(?pw,4)
sum(?tp,?pw,?lp)
->
(?s ex:finalGrade ?tp)
]
Example 3 (Hybrid Rule)
A student is diligent if it has at least 60 credit points.
@prefix ex: http://example.com/school#
[diligent: (?s rdf:type ex:Student)
->
[diligent: (?s rdf:type ex:DiligentStudent)
<-
(?s ex:totalCredits ?c)
greaterThan(?c,60)
]
]
There are some namespaces used in rules which are internally defined by JenaRules, and is no need to redefine them again in the rule file (i.e. rdf, rdfs). There is possible to import other rule files by using the @include directive (see Example 4). It is followed by a URL where the rule file is available. This has to be a real URL, since if no file is found there, an exception is trowed. Also, a set of predefined rules are available by using special names in place of the URL in @include directive (i.e. RDFS, OWL, OWLMicro, OWLMini).
Example 4 : Using the @include directive
@prefix ex: http://example.com/school <br /><br />
@include <http://example.com/myRuleFile.rules/> <br />
@include <RDFS>
.....
Loading and executing your rules
We have seen how we can write simple rules in Jena syntax. Now, we will learn how to execute those rules over a set of RDF facts. We consider previous rules and we define a set of facts for some students. Is important to notice that is is not mandatory to define a schema (RDFS) for those RDF facts.
Example 5 : RDF Facts Example
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ex="http://example.com/school#">
<ex:Student rdf:about="student1">
<ex:name>John Smith</ex:name>
<ex:totalCredits rdf:datatype="http://www.w3.org/2001/XMLSchema#int">70</ex:totalCredits>
</ex:Student>
<ex:Student rdf:about="student2">
<ex:name>Mike Stone</ex:name>
<ex:totalCredits rdf:datatype="http://www.w3.org/2001/XMLSchema#int">40</ex:totalCredits>
</ex:Student>
<ex:Student rdf:about="student3">
<ex:name>Tom Brenner</ex:name>
<ex:totalCredits rdf:datatype="http://www.w3.org/2001/XMLSchema#int">70</ex:totalCredits>
<ex:writtenExamPoints rdf:datatype="http://www.w3.org/2001/XMLSchema#int">12</ex:writtenExamPoints>
<ex:labExamPoints rdf:datatype="http://www.w3.org/2001/XMLSchema#int">8</ex:labExamPoints>
</ex:Student>
</rdf:RDF>
At this moment we have a set of rules, a set of facts and we need to use the engine in order to obtain the result of the execution of rules. In order to do this, we have to create a simple Java application witch use Jena API. We have to follow a few steps: load RDF data, load rules, create and configure the engine, execute the engine, watch the results. The following Java class contain a method, namelly runEngine, taking parameters the URL for rules file and the URL to RDF facts file and after execution of the engine, the results are written down to the Java console:
Example 6 : Create and execute the inference engine
package test;
import com.hp.hpl.jena.rdf.model.InfModel;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.reasoner.Reasoner;
import com.hp.hpl.jena.reasoner.rulesys.GenericRuleReasonerFactory;
import com.hp.hpl.jena.util.FileManager;
import com.hp.hpl.jena.vocabulary.ReasonerVocabulary;
public class TestJena {
// the reasoner
Reasoner reasoner;
// the RDF model
Model model;
// a resource used to configure the engine
Resource configuration;
// the inference model
InfModel infModel;
// the default constructor
public TestJena() {
this.reasoner = null;
this.model = null;
this.configuration = null;
this.infModel = null;
}
// create and execute the rule engine
public void runEngine(String ruleFile, String rdfFile) {
// create a default model (empty model)
model = ModelFactory.createDefaultModel();
// create a resource (empty resource)
configuration = model.createResource();
// set engine mode
configuration.addProperty(ReasonerVocabulary.PROPruleMode, "hybrid");
// set the rules file
configuration.addProperty(ReasonerVocabulary.PROPruleSet, ruleFile);
// Create an instance of such a reasoner
reasoner = GenericRuleReasonerFactory.theInstance().create(configuration);
// Load RDF test data
model = FileManager.get().loadModel(rdfFile);
// create the inference model
infModel = ModelFactory.createInfModel(reasoner, model);
// force starting the rule execution
infModel.prepare();
// write down the results in RDF/XML form
infModel.write(System.out);
}
}
We see that the engine mode is set to hybrid. In this mode, all types of rules: backward, forward and hybrid are executed. Other available values for this property are: forward, forwardRETE and backward. After create the class from Example 6, all we need to do now is to instantiate this class and execute the runEngine method with some parameters.
Example 7 : Main class
package test;
public class Test {
public static void main(String[] args) {
TestJena tester = new TestJena();
tester.runEngine("data/test.rules", "data/test.rdf");
}
}
We can see that the rule from Example 1 and the rule from Example 3 deducts the same facts. In order to test the example, you can use one by one the two rules. After the rules are executed over the set of facts from Example 5, we will obtain new facts (see Example 8). Those rules deduct that the student1 and student3 are of type ex:DiligentStudent, and for student3 also the value for ex:finalGrade property is calculated. The result is depicted below:
Example 8 : Inference results
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ex="http://example.com/school#">
<ex:Student rdf:about="student1">
<ex:name>John Smith</ex:name>
<ex:totalCredits rdf:datatype="http://www.w3.org/2001/XMLSchema#int">70</ex:totalCredits>
<!-- added fact -->
<rdf:type rdf:resource="http://example.com/school#DiligentStudent"/>
</ex:Student>
<ex:Student rdf:about="student2">
<ex:name>Mike Stone</ex:name>
<ex:totalCredits rdf:datatype="http://www.w3.org/2001/XMLSchema#int">40</ex:totalCredits>
</ex:Student>
<ex:Student rdf:about="student3">
<ex:name>Tom Brenner</ex:name>
<ex:totalCredits rdf:datatype="http://www.w3.org/2001/XMLSchema#int">70</ex:totalCredits>
<ex:writtenExamPoints rdf:datatype="http://www.w3.org/2001/XMLSchema#int">12</ex:writtenExamPoints>
<ex:labExamPoints rdf:datatype="http://www.w3.org/2001/XMLSchema#int">8</ex:labExamPoints>
<!-- added facts -->
<rdf:type rdf:resource="http://example.com/school#DiligentStudent"/>
<ex:finalGrade rdf:datatype="http://www.w3.org/2001/XMLSchema#int">20</ex:finalGrade>
</ex:Student>
</rdf:RDF>
See also: Deploying Jena Rules Applications
The rule engine
Jena implements three different rule engines: backward and forward and hybrid. First, the backward engine allows execution of the backward rules. The engine mode has to be set to backward. The second is a forward engine and allows the execution of the forward rules (expressed by ->) and backward rules (expressed by <-), but the backward rules are interpreted as forward rules. For this engine, the engine mode has to be set to forwardRETE or forward mode. The third engine is a hybrid one, and allows execution of forward rules, backward rules and hybrid rules. In Fig. 1, we can see the flow of rules execution using this engine.
Jena2 includes an RDFS reasoner (RDFSRuleReasoner) which supports almost all of the RDFS entailments described by the RDF Core working group. This reasoner is accessed using ModelFactory.createRDFSModel or manually via ReasonerRegistery.getRDFSReasoner(). The RDFSRuleReasoner can be configured to work at three different compliance levels:
- Full - This implements all of the RDFS axioms and closure rules with the exception of bNode (blank nodes) entailments and datatypes.
- Default - This omits the expensive checks for container membership properties and the everything is a resource and everything used as a property is one rules.
- Simple - This implements just the transitive closure of subPropertyOf and subClassOf relations, the domain and range entailments and the implications of subPropertyOf and subClassOf. It omits all of the axioms.
The level can be set using the setParameter call:
reasoner.setParameter(ReasonerVocabulary.PROPsetRDFSLevel,
ReasonerVocabulary.RDFS_SIMPLE);
or by constructing an RDF configuration description and passing that to the RDFSRuleReasonerFactory e.g.
Resource config = ModelFactory.createDefaultModel()
.createResource()
.addProperty(ReasonerVocabulary.PROPsetRDFSLevel, "simple");
Reasoner reasoner = RDFSRuleReasonerFactory.theInstance()Create(config);
The RDFSReasoner can be obtained in two ways:
Reasoner reasoner = ReasonerRegistry.getRDFSReasoner();
or
Reasoner reasoner = RDFSRuleReasonerFactory.theInstance().create(null);
and, then the reasoner ca be used to obtain the InfModel instance (see Example 6).
The structure of the Jena inference machine is depicted in Fig2.
See also: Jena RETE Algorithm
Bibliography
- Brian McBride. Jena: Implementing the RDF Model and Syntax Specification. 3rd Annual Conference on World Wide Web Applications, 5-7 September 2001.
- Dave Reynolds, Jena Rules (slides), Jena User Conference, May 10-11, 2006, Bristol, UK.



