Annotation Generator

From Filtered Push Wiki
(Redirected from Rdf Handler)
Jump to: navigation, search


Use case

Imagine that an RDF-aware Java application wishes to provide RDF that corresponds to filling in details of a putative Person object, with a fragment of code like

 
         // Create an instance of Person
         Person person = new Person(new GregorianCalendar(1980, 4, 16));
         person.setFirstName("David");
         person.setLastName("Lowery");
         person.setEmail("lowery@cs.umb.edu");
         person.setOccupation("Project Programmer");
 
         person.setRdfAbout("David_Lowery");
 
         //now do something to output RDF
and that the application requires output couched in some standard vocabulary, e.g. the Friend of a Friend (FOAF) ontology, resulting in:

 <rdf:RDF
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:foaf="http://xmlns.com/foaf/0.1/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
     xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" > 
   <rdf:Description rdf:about="http://www.etaxonomy.org#David_Lowery">
     <foaf:firstName>David</foaf:firstName>
     <foaf:lastName>Lowery</foaf:lastName>
     <foaf:mbox>lowery@cs.umb.edu</foaf:mbox>
     <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
   </rdf:Description>
 </rdf:RDF>

This page describes the use of the Filtered Push RdfHandler, which provides a small interface RdfBean that the application class must implement, and a set of packages, principally org.filteredpush.rdf.handler, that invoke the Apache Jena framework to produce and serialize the RDF. Somewhat more precisely, the package produces a Jena Model and, in the example in this tutorial, invokes Jena to serialize.. Other outcomes from the Jena Model are also possible, for example storing the result in a triple store managed by Jena.

The way in which the RdfBean is mapped to a particular form of RDF is specified by an XML configuration we call the Model Descriptor.

What follows is a more detailed discussion of this use case.

Introduction

JavaDocs: http://filteredpush.sourceforge.net/docs/rdfhandler/

The RdfHandler (packages in org.filteredpush.rdf) is a library designed to create a Jena Model from the members of Java objects. You can use any set of Java objects you wish, however the RdfHandler requires that they implement that RdfBean interface. The handler uses an xml description of the desired rdf model called the Model Descriptor. The Model Descriptor defines the resources, properties (and their relations to class members) and ontologies used in the model. Java and/or PHP classes for use by clients can be generated from the Model Descriptor xml files such that fields and methods in these classes can be mapped to ontologies. Below is a diagram that gives an overview of the process:

Handler1.png

Implement RdfBean

The handler will only handle java classes that implement the RdfBean interface. Each RdfBean can have zero, one or more rdf types, a subject name which is used by the handler to for identifying how the java object is to be processed and an rdf about.

The rdf types assigned to a given Java object must be a string preeceeded by the namespace prefix of your choosing (such as foaf:Person or ao:Annotation). The subject name can be any String name.

NOTE: Subject in this context is different that the subject used in the rdf model. The subject name as it pertains to an Rdf Bean is only used for identification and the text of this property is not used in the final model output.

There are three method implementations required:

  • List<String> getRdfTypes() - Must return a list of rdf types as Strings.
  • public String getSubjectName() - The subject name (maps to the subject name defined in the xml for an rdf resource).
  • public String getRdfAbout() - The rdf about uri.

All java classes that you plan to use with the handler must implement this interface. Classes that are generated by the Generation Utility implement this interface by default. The subject name, rdf types and rdf about values may be defined as static fields and returned by the getters. Alternatively, you could create setters and set the subject name, rdf types and rdf about at runtime.

The RdfBean implementation below gives an example of a class that might be used to represent a person:

 
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.List;
 
 import org.filteredpush.rdf.handler.RdfBean;
 
 public class Person implements RdfBean {
     private String firstName;
     private String lastName;
     private String email;
     private Calendar dob;
 
     private String occupation;
 
     public Person(Calendar dob) {
         this.dob = dob;
     }
 
     public String getFirstName() {
         return firstName;
     }
 
     public void setFirstName(String firstName) {
         this.firstName = firstName;
     }
 
     public String getLastName() {
         return lastName;
     }
 
     public void setLastName(String lastName) {
         this.lastName = lastName;
     }
 
     public String getEmail() {
         return email;
     }
 
     public void setEmail(String email) {
         this.email = email;
     }
 
     public String getOccupation() {
        return occupation;
     }
 
     public void setOccupation(String occupation) {
        this.occupation = occupation;
     }
 
     public int getAge() {
         Calendar today = new GregorianCalendar();
         int ageYear = (today.get(Calendar.YEAR) - dob.get(Calendar.YEAR));
         int ageMonth = (today.get(Calendar.MONTH) - dob.get(Calendar.MONTH));
         if (ageMonth < 0) {
            ageYear--;
         }
         return ageYear;
     }
 }

Xml Model Descriptor

For each Jena model (or rdf instance document) you wish to describe, you must create an xml model descriptor file for use by the Handler.

The root element of this document must be the <handlerModel>. A <handlerModel> contains an <ontologies> element and a <resources> element. Additionally, you may include an optional <validators> element for defining validation:

<handlerModel>
  <ontologies>
    ...
  </ontologies>
  <validators>
    ...
  </validators>
  <resources>
    ...
  </resources>
</handlerModel>

As a first step in constructing a model descriptor configuration, we define the ontologies used by specifying the namespace uris and prefixes. This information is represented as a series of <ontology> elements with prefix and uri defined as attributes of each. The example below is from the model descriptor for an applepie annotation.

<ontologies>
  <ontology prefix="ao" uri="http://purl.org/ao/" />
  <ontology prefix="aod" uri="http://etaxonomy.org/ontologies/ao/aod.owl#" />
  <ontology prefix="pav" uri="http://purl.org/pav/" />
  <ontology prefix="foaf" uri="http://xmlns.com/foaf/0.1/" />
  <ontology prefix="dcterms" uri="http://purl.org/dc/terms/" />
  <ontology prefix="dwcFPModel" uri="http://etaxonomy.org/ontologies/ao/dwcFPModel.owl#" />
</ontologies>

You do not have to include <ontology> elements for rdf rdfs and xsd terms (the handler defines these as part of its default behavior).

Next we define <resource> elements. Each resource contains a value for the subject attribute. The value of the subject attribute corresponds to the value of the subject name returned by getSubjectName() in the RdfBean. If you choose to implement a setter for the subject name and give a bean its subject name on a per instance basis, you can define multiple representations of the same class by creating <resource> elements in the descriptor with different subject names:

<resources>
  <resource subject="Annotation">
    ...
  </resource>
</resources>

In addition to linking an instance of a Java class to a resource descriptor in the xml file, the subject name can also be referenced as the object of an object property elsewhere in the descriptor (more on this later).

The <resource> element contains a series of <property> elements, each of which contain a <subject> and a <predicate>. There are three types of properties that can be described, bound, unbound and object properties. Properties may be bound or unbound depending on where the object of the predicate is defined. Unbound properties are any property whose object is obtained from a java class method return value or field. Bound properties have their object defined as a text value in the xml descriptor. An object property references another resource by its subject name as defined elsewhere in the model descriptor.

All properties contain the <predicate> element. The content of <predicate> must be of the form "prefix:property" (the namespace prefix is defined in the <ontologies> portion of the xml descriptor). The fully qualified url will be expanded by the handler.

In the case of an unbound property, the <property> elements are mapped to fields or methods in a set of generated or user-defined java classes. By default classes are named after the value of the resource subject attribute and placed in the package that is set as the value of the resources element java-package attribute. The Generation Tool uses the subject and java-package attribute values to name and place the generated classes in the correct package.

If using the Generation Tool, the top-level class must be marked as the root node (so that the generation code knows where to place the Jaxb @XmlRootElement annotation). This can be done using the rootNode attribute of the resource element (set its value to true):

<resources java-package="org.filteredpush.client.model.generated">
  <resource subject="Annotation" rootNode="true">
    <properties>
      <property>
        <predicate>pav:createdBy</predicate>
        <object describes="Annotator" />
      </property>
      <property>
        <predicate>pav:createdWith</predicate>
        <object describes="Generator" />
      </property>
      ...
    </properties>
  </resource>
  ...
</resources>

If a set of properties is using a user-defined class with a name that differs from the subject attribute value, it must have an explicit java-class attribute with a fully qualified class name as its value. You may have several groups of properties defined in a single resource element, each with different class names or no class name. Each of these groups is a set of <property> elements defined in a <properties> element.

<resource subject="Annotation">
  <properties>
    <property>
      <predicate>pav:createdBy</predicate>
      <object describes="Annotator" />
    </property>
    <property>
      <predicate>aod:annotatesResource</predicate>
      <object describes="Subject" />
    </property>
    ...
  </properties>

  <properties java-class="org.filteredpush.client.util.AnnotationHelper">
    <property>
      <predicate>pav:createdOn</predicate>
      <object datatype="xsd:dateTime" java-method="getToday" />
    </property>
  </properties>
</resource>

Previously mentioned, there are three ways to configure the property elements:

Object properties - contain an <object> element that has a describes attribute. The value of describes is the subject name of any other resource defined elsewhere in the descriptor. If the object is a container (such as rdf:bag) the object would have a container attribute with a value "bag" and a contains attribute with a value that is the subject name of the objects that are to be contained in the bag.

<property>
  <predicate>pav:createdBy</predicate>
  <object describes="Annotator" />
</property>

or

<property>
  <predicate>aod:annotatesResource</predicate>
  <object container="bag" contains="Occurrence" />
</property>

Unbound properties - the <properties> tag has a value for the java-class that designates the class that contains methods or fields from which to obtain the property values. The <object> child of each of the <property> elements within a group of unbound properties has a java-method or a java-field attribute. The value value of each of these attributes corresponds to the method or field in the java class defined in the opening <properties> tag. In the examples below, references is a field in the class AnnotationSubject and getToday is a method in class AnnotationHelper. If a field is marked private but has a public getter method, you can still specify the field name as a value of java-field (the Handler will automatically find the correct getter method given the field name)

<properties java-class="org.filteredpush.client.model.AnnotationSubject">
  <property>
    <predicate>dcterms:references</predicate>
    <object java-field="references" />
  </property>
  ...
</properties>

or

<properties java-class="org.filteredpush.client.util.AnnotationHelper">
  <property>
    <predicate>pav:createdOn</predicate>
    <object datatype="xsd:dateTime" java-method="getToday" />
  </property>
</properties>

Bound Properties - the object element simply contains the value of the property as a text string. May or may not have the datatype attribute.

<property>
  <predicate>aod:asText</predicate>
  <object datatype="xsd:string">Hello World.</object>
</property>

Any <object> element (except for the object of an object property which may only have the describes attribute and no content) may have the datatype and/or lang attributes.

A complete example of the simple model descriptor required to produce a Jean model from the Person class from the previous section is presented below:

<ontologies>
  <ontology prefix="foaf" uri="http://xmlns.com/foaf/0.1/" />
<ontologies>

<resources>
  <resource subject="Person">
    <properties java-class="org.filteredpush.rdf.examples.Person">
      <property>
        <predicate>foaf:firstName</predicate>
        <object java-field="firstName"/>
      </property>
      <property>
        <predicate>foaf:lastName</predicate>
        <object java-field="lastName"/>
      </property>      
      <property>
        <predicate>foaf:mbox</predicate>
        <object java-field="email"/>
      </property>
      <property>
        <predicate>foaf:mbox</predicate>
        <object java-method="getAge"/>
      </property>
    </properties>
  </resource>
</resources>


Validators

In addition to defining the unbound properties and mapping them to String fields of Java classes, an xml model descriptor author can specify custom validation behavior to be executed by the RdfHandler at generation time (such as date parsing).

A validator for use with the RdfHandler must extend the RdfBeanValidator abstract class (org.filteredpush.rdf.handler.RdfBeanValidator) and must override the validate(String value) method. This method takes a String argument that is the value of the property from the model class and will either convert it to the appropriately formatted String or throw an Exception if it is unable to perform conversion and the input is unrecognized.

An example of a simple validator that will take a date formatted as "yyyy-MM-dd" and convert it to the month string only in the final serialization of rdf is presented below:

 
 public class DateParser extends RdfBeanValidator {

      public String validate(String value) throws ParseException {
           DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
           Date date = format.parse(dateStr);
           Calendar calendar = Calendar.getInstance();
           calendar.setTime(date);
           
           // Automatically format date as month if it's valid (i.e. Janurary
           return calendar.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.US);
      }
 }


Once an implementation of RdfBeanValidator has been created, it can be declared in the descriptor xml and assigned to unbound properties. In the previous section, the <validators> element was mentioned briefly. This element contains validator definitions in the form of <validator> elements. Each validator element must have an associated name (defined as the value of the name attribute) and a fully qualified Java class name (as the value of the java-class attribute. An example of a <validators> definition containing only one validator for the date parsing example above is:

<validators>
  <validator name="DateParser" java-class="org.filteredpush.client.util.DateParser" java-type="java.util.Date" />
</validators>

This validator can be referenced by its name, DateParser, in the unbound properties of the descriptor. The validator attribute of the object element references the validator to be used when parsing the string field of the associated property value. Below is an example of a resource that contains an unbound property that uses the DateParser validator:

<resource subject="Context">
  <properties>
    ...
    <property>
      <predicate>dwcFP:dateIdentified</predicate>
      <object validator="DateParser" />
    </property>
    ...
  </properties>
</resource>

When a value for this property is supplied via the model classes or an xml/json serialization of the model in the format "yyyy-MM-dd" it will automatically be converted to the long month name format using the validator. Otherwise, if the format supplied is invalid or unrecognized, an exception is thrown and rdf generation will fail.

Using RdfBeanHandler

Below is an overview of the packages that make up the RdfHandler. The handler package depends on classes in the reflection and processing packages. If you are curious about how the RdfHandler is implemented or need to make changes to the RdfBeanHandler itself you can look at the documentation for the Handler Reflection and Handler Processing. This section describes how to use the code in the handler package (specifically RdfBeanHandler).

Handler2.png

The constructor for RdfBeanHandler (in org.filteredpush.rdf.handler) takes two arguments. The first is the top-level Java class that implements RdfBean and has references to other RdfBeans and the second is the xml descriptor file path:

RdfBeanHandler handler = new RdfBeanHandler(SimpleAnnotation.class, "identification.xml");

RdfBeanHandler starts by using reflection to gather information about all the classes related to the top-level class. Classes are considered related if they are in the same package as the top-level class and are assigned as the type of a field or as a method return type. The handler will find related classes recursively starting with the top-level class and examining all methods and fields.

Once the classes and their members have been discovered, they are placed into a map which is stored until the RdfBeanHandler is destroyed and garbage collected. For this reason, constructing an RdfBeanHandler has a bit of overhead and should only be done once. You can reuse the handler and it will not perform any more Java reflection. This design allows you to create multiple Jena models using instances of the same classes and the same xml config without the overhead of reflection for each instance. If you need to create models for a different set of classes or a differet nt config, you must construct a new handler specifically for that purpose.

Each RdfBean instance must return a value for getRdfAbout() and getSubjectName() (getRdfTypes() must return List<String> but it can be empty). Once you have an instance of an RdfBean, you can use the handler to create a Jena model with a call to handler.createJenaModel(...). This method takes two arguments. The first is an instance of the top-level class (defined in the call to the RdfBeanHandler constructor) and the second is the base URI for all the resources in the model (if using the model to generate a document, the base URI of the document).

Below is an example of creating a Jena model from an applepie annotation rdf bean. The code that populates fields and methods of the SimpleAnnotation and adds references to other objects in the class hirearchy is omitted for simplicity.

SimpleAnnotation annotation = new SimpleAnnotation();
...

Model model = handler.createJenaModel(annotation, "http://etaxonomy.org/ontologies/ao/" + UUID.randomUUID().toString());

At this point you can use jena to do the rest, for example, create rdf/xml:

StringWriter sw = new StringWriter();
model.write(sw);

The complete code to take our Person class and the descriptor.xml and output a Jena model as rdf/xml is as follows:

 
 import java.util.GregorianCalendar;
 import org.filteredpush.rdf.handler.RdfBeanHandler;
 import com.hp.hpl.jena.rdf.model.Model;
 
 public class PersonToRDF {
     public static void main(String[] args) throws Exception {
         RdfBeanHandler handler = new RdfBeanHandler(Person.class, "descriptor.xml");
 
         // Create an instance of Person
         Person person = new Person(new GregorianCalendar(1980, 4, 16));
         person.setFirstName("David");
         person.setLastName("Lowery");
         person.setEmail("lowery@cs.umb.edu");
         person.setOccupation("Project Programmer");
 
         person.setRdfAbout("David_Lowery");
 
         // Use the rdf handler to create a jena model and output rdf/xml
         Model model = handler.createJenaModel(person, "http://www.etaxonomy.org");
         model.write(System.out);
     }
 }

The jena model output as rdf/xml is shown below:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:foaf="http://xmlns.com/foaf/0.1/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" > 
  <rdf:Description rdf:about="http://www.etaxonomy.org#David_Lowery">
    <foaf:firstName>David</foaf:firstName>
    <foaf:lastName>Lowery</foaf:lastName>
    <foaf:mbox>lowery@cs.umb.edu</foaf:mbox>
    <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
  </rdf:Description>
</rdf:RDF>


RdfHandler supports java class inheritance, interfaces and abstract classes. Any class that is a subclass of an RdfBean must be used explicitly in one of the java-class properties of the xml descriptor. The RdfHandler determines which classes you can use based on which ones have been defined in the xml.

RdfHandler Web Service

The FP-AnnotationWS project (https://filteredpush.svn.sourceforge.net/svnroot/FP-Deployments/FP-AnnotationWS/ in our svn repository) exposes the RdfHandler as a RESTful Web Service to remote clients (in the case of FilteredPush, Morphbank and Symbiota). The webservice can be used with the set of autogenerated or user-defined Java/PHP classes. Clients written in other languages can also invoke the web service by providing xml or json that is an appropriate serialization of the model defined by the descriptor xml files.

The web service is configured to host several instances of the RdfHandler class (one per descriptor xml). These handlers can be named and configured (handler.properties file) with a descriptor and a set of corresponding classes. An example of the handler.properties file used in the FilteredPush network can be found in the FP-RdfHandler project (in src/main/resources).

The handler.properties file has 4 basic properties associated with each configured handler:

  1. {handlername}.bridge - if using the bridge model, specify the top level bridge class (see #Bridge_Model)
  2. {handlername}.class - top-level class of the model (if neither rootNode or java-package are present in the resources element of the descriptor.xml)
  3. {handlername}.descriptor - the name of the xml descriptor file for this particular handler (must be on the classpath of the webservice upon deployment)
  4. {handlername}.baseuri - the base uri to be used for the rdf resources (in rdf/xml this corresponds to the rdf about attribute)

Once configured, the handler.properties file must be on the classpath of the web service deployment. Clients can reference a handler for generating rdf/xml at a url such as:

http://localhost:8080/annotationws/rest/generate/{handlername}

As mentioned previously, the handler will accept either xml or json serializations of the model objects. In the case of Java objects (either generated or user-defined) the objects may have Jaxb annotations such as @XmlRootElement. These objects can be supplied to the webservice if invoked from Java by using the features of the JAX-WS APIs. For example, an annotation object that has an @XmlRootElement annotation can be marshalled to xml with the following code:

JAXBContext context = JAXBContext.newInstance(annotation.getClass());
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(annotation, writer);
String xml = writer.toString();

The string object "xml" can then be passed as an argument to the webservice:

ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource("http://localhost:8080/annotationws/");
String response = service.path("rest").path("generate")
        .path("identification").type(MediaType.APPLICATION_XML).accept(MediaType.APPLICATION_XML)
        .entity(xml).post(String.class);

In the example above, the string literal "http://localhost:8080/annotationws/" represents the url of the deployed webservice and "identification" in the path refers to the handler by this name (as defined in handler.properties). The string object response will contain the rdf/xml returned by the webservice.

The handler webservice can also be invoked from clients written in other languages (such as PHP) by using the language specific mechanisms for http POST. In PHP it is most convenient to POST json that is produced via a call to json_decode with a PHP class representation of the model (although one could construct an xml string that corresponds to the same model and post that instead).

The generation utility (see #Code_Generation) will provide PHP clients with a set of PHP classes that represent the model as well as an "AnnotationGenerator.php" that invokes json_encode and performs a curl_post to the web service. The AnnotationGenerator can be invoked via a PHP client as follows:

$annotation = new Annotation();
...

$generator = new AnnotationGenerator("http://localhost:8080/annotationws/rest/generate/identification");
$rdf = $generator->generateRdfXml($annotation);

The example above uses a model object represented by $annotation and a handler web service endpoint of http://localhost:8080/annotationws/rest/generate/identification. The generateRdfXml method will json_encode the model objects and POST the resulting json to the handler web service. After the POST succeeds $rdf should contain the rdf/xml document that was defined by the descriptor xml.

If you wish to supply raw xml to the web service (without using intermediary model objects) the web service can provide someone wishing to implement a client in this way with an xml schema for each handler's model. This is also useful if you plan to use a client written in a language other than PHP or Java and need to make your own model objects using this language. The schema for any given handler can be accessed via http GET like so:

http://localhost:8080/annotationws/rest/generate/{handlername}/schema

The above request will return the xsd representation of the model that the handler accepts.

Code Generation

A set of Java and/or PHP classes can be generated from a model descriptor xml file for use with client and the RdfHandler webservice. The generation utility can be found in the org.filteredpush.rdf.generation package (the main class is DescriptorJavaClasses). A build.xml file is provided in the project root directory with two targets dedicated to generating code from descriptors. If you have downloaded a jar file you can start it from the command-line. Methods of invoking the code generation utility are detailed below.

Simple Model

Running the utility in the following manner will create a set of simple model classes for clients written in Java or PHP. Changes to the descriptor xml will require you to re-run this utility and generate new classes. These new classes must then be distributed to clients and the old ones replaced. See the documentation for "Bridge Model" below if you would like to deploy the Annotation web service in a way that partially shields the clients from simple changes in the config (by using a set of bridge classes).

If you are using the ant tasks in the build.xml file provided with the project you must first edit the build.properties file. Set model.package to the name of the Java package that the generated classes should be in (i.e. org.filteredpush.client.model) and set descriptors.dir to the directory containing descriptor xml files from which code will be generated. The generated.package property can be ignored for the simple model generation case.

Once you have set the properties in the build.properties file you can invoke the ant build target named "gen-model-simple" via the following:

ant gen-model-simple

This will create the following directories and files in the target/generated directory:

  • java/dist - contains a jar file that contains the model (generated-model.jar) that can be distributed to java clients and included on the build path of the rdfhandler web service
  • java/classes - contains compiled classes for the model
  • java/src/ - contains the generated source files for the model
  • php - contains model classes and the AnnotationGenerator for use by PHP clients

If you have downloaded the jar file (i.e. FP-RdfHandler-0.0.1-SNAPSHOT-jar-with-dependencies.jar) you may start the code generation from the command-line by using the following command:

java -jar FP-RdfHandler-0.0.1-SNAPSHOT-jar-with-dependencies.jar {output_directory} {descriptors_directory} {model_package_name}

For a simple model, the utility takes the three arguments shown above where the output directory is the directory that the utility will output to, the descriptors directory is the location with one or more descriptor xml files and model package name is the package name (i.e. org.filteredpush.client.model)

Unlike the ant build, the jar alone will only generate the java/src and php directories in the directory supplied as an argument for output and will not compile the java sources.

Bridge Model

If you plan on making changes to the descriptor xml files your model classes will need to be regenerated each time or the RdfHandler will not be able to serialize them to rdf properly. This will require you to update each client with the newly generated classes each time an update is made. If you wish to generate and use model classes in a more loosely coupled manner you can use the bridge model method of generation.

In addition to the model classes, a separate set of bridge classes are generated when the -bridge option is supplied to the code generation utility. The first time you generate a set of model classes from the descriptor xml, you can distribute them to clients and they will correspond to the interface defined in the bridge classes. The bridge classes act as wrapper classes for the original model classes. The next time an update is performed on the descriptor, a new set of classes must be generated and the bridge classes can be modified to wrap the newly generated classes while stile conforming to the interface for the model.

The bridge classes adapt the older model (the one that was originally provided to the clients) to the newer model (generated after changes were made to the descriptor). This is useful when you would like to make a series of small updates very frequently without having those changes affect the clients. When large changes occur or many small ones have occurred, a new set of bridge classes can be generated and the newer model can then be distributed to clients. The benefit of this approach is that updates to client code do not have to occur as frequently as the updates to the descriptor.

If you will generate the model classes using the ant build, make sure that you set the model.package and descriptors.dir properties to the correct values (see #Simple_Model). You will also need to set the generated.package property to the desired package name for generated classes (i.e. org.filteredpush.client.model.generated). The bridge classes will be generated with the package name supplied as the value of the model.package property and the generated model will be placed in the package name supplied as the value of generated.package.

Once you have set these properties you can run the ant task via:

ant gen-model-bridge

This will create the following directories and files in the target/generated directory:

  • server/java/dist - contains a jar file that contains the model (server-generated-model.jar) that can be included on the build path of the rdfhandler web service
  • server/java/classes - contains compiled classes for the model
  • server/java/src/ - contains the generated source files for the model
  • client/java/dist - contains a jar file that contains the model (client-generated-model.jar) that can be distributed to java clients
  • client/java/classes - contains compiled classes for the model
  • client/java/src/ - contains the generated source files for the model
  • client/php - contains model classes and the AnnotationGenerator for use by PHP clients

Distribute the client-generated-model.jar file, the classes in the client directory or the sources in the client directory to the Java client(s). Put the server-generated-model.jar file, classes or sources in the server directory on the classpath of the Annotation Web Service or other server application that invokes the RdfHandler. Put the contents of the php directory on the include path of any php clients.

If you have downloaded the jar file for the RdfHandler, you may invoke the model and bridge code generation via the following command:

java -jar FP-RdfHandler-0.0.1-SNAPSHOT-jar-with-dependencies.jar -bridge {output_directory} {descriptors_directory} {model_package_name} {generated_package_name}

For a bridge model, the utility requires the -bridge option and takes the four arguments shown above where the output directory is the directory that the utility will output to, the descriptors directory is the location with one or more descriptor xml files and model package name is the package name that the bridge classes will use (i.e. org.filteredpush.client.model) and the generated_package_name is the package name that the generated model classes will use (i.e. org.filteredpush.client.model.generated)

Unlike the ant build, the jar alone will only generate server/java/src/, client/java/src/ and client/php in the directory supplied as an argument for output and will not compile the java sources.