BocaProgrammingModel

From IBM Semantic Layered Research Platform

Jump to: navigation, search

In this section we take you through a series of sample Java programs that make use of the Boca API. Rather than going into great detail about the concepts at work, we refer the reader to the corresponding more detailed sections. These examples and brief descriptions are meant to allow developers to quickly begin prototyping Semantic Web client and web applications using Boca. Nevertheless, the developer is encouraged to study the sections detailing the specific semantics and mechanics of Boca.


Contents

Compiling and running examples

Before delving in, we quickly describe how to run the examples. In BocaSetup we showed how to install, configure and run the Boca server. To run the examples, all steps described therein should be completed.

Binary distribution

1 - Start the Boca server as in BocaSetup.

2 - In a new command window, change directory to c:\boca-1.7\examples. Make sure javac from from a Java 5 JDK is in your path.

3 - run compile-examples.bat or compile-examples.sh

4 - to run an example, use run-example.bat or run-example.sh with the package unqualified classname as an argument. Available examples can be found in c:\boca-1.7\examples\com\ibm\adtech\boca\sample\

For example:

   run-example RemoteModelWebService

Source distribution

Eclipse is the easiest way to run and modify the samples. Note that the example code contains extensive comments and descriptions that has been stripped from the code listings on this page (but reproduced in the explanations on this page).

1 - Open the project com.ibm.adtech.boca.sample in the Eclipse workspace created during source installation.

2 - Run any of the classes in com.ibm.adtech.boca.sample as a Java application

The Basics: DatasetServices, named graphs and models

The first three examples demonstrate how to connect to Boca and gain access to named graphs via a Jena-style interface. We also briefly show the two different methods for connecting to Boca: the Web Service client and the embedded client. In BocaArchitecture we discuss these in greater detail. For now, simply understand that the Web Service client uses a Web Service call to the server to perform all operations against the database layer, while the embedded client speaks directly to the database layer through calls in the same JVM. The two use cases differ only in configuration, not code. The Web Service client is typically used for java-based client multi-user applications while the embedded client API is used in server-side applications which themselves use Boca but expose other interfaces to clients.

Example 1: DatasetService and remote models in action

If you've been patient and made it through all of the User Guide up to now, you've done a lot of reading without seeing a line of code. So without further ado, let's now dive into a first example of using the Boca API. To run the following example outside of Eclipse, it is best to open two command windows. In one window, start Boca as described in BocaSetup, and in the second switch to c:\boca-1.7\examples, and type

   run-example RemoteModelWebService

(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.rdf.model.Property;
(7)   import com.hp.hpl.jena.rdf.model.Resource;
(8)   import com.ibm.adtech.boca.client.DatasetService;
(9)   import com.ibm.adtech.boca.client.NamedGraphModel;
(10)  
(11)  public class RemoteModelWebService {
(12)  
(13)      public static void main(String[] args) throws Exception {
(14)  
(15)          DatasetService datasetService = null;
(16)          try {
(17)  
(18)              Properties webserviceProperties = new Properties();
(19)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(20)  
(21)              datasetService = new DatasetService(webserviceProperties);
(22)  
(23)              String namedGraphURI = "http://example.org/ng1";
(24)              boolean createIfNecessary = true;
(25)              NamedGraphModel remoteModel = null;
(26)              try {
(27)                  remoteModel = datasetService.getRemoteModel(namedGraphURI, createIfNecessary);
(28)                  Resource res1 = remoteModel.createResource("http://example.org/res1");
(29)                  Property prop1 = remoteModel.createProperty("http://example.org/prop1");
(30)  
(31)                  remoteModel.begin();
(32)                  try {
(33)                      remoteModel.add(res1, prop1, "value1");
(34)                      remoteModel.add(res1, prop1, "value2");
(35)                      remoteModel.commit();
(36)                  } catch (Exception e) {
(37)                      remoteModel.abort();
(38)                      throw e;
(39)                  }
(40)  
(41)                  datasetService.getDatasetReplicator().replicate(true);
(42)              } finally {
(43)                  if (remoteModel != null)
(44)                      remoteModel.close();
(45)              }
(46)          } finally {
(47)              if (datasetService != null) {
(48)                  datasetService.close();
(49)              }
(50)          }
(51)      }
(52)  
(53)  }

The first thing that jumps out of this code is the large number of try-catch and try-finally blocks. These are used to manage Boca resource handles and connections--we'll get to them in a minute. For now, let's focus on the interesting stuff. Line (15) declares a DatasetService, the top-level object in the Boca API. A single instance of DatasetService manages connections to Boca, JMS messaging, and instances of all other objects. In general, most applications will use a single instance of DatasetService shared between all application components. Because of this, careful resource management is critical to long-running applications. (18-21) instantiate the DatasetService. Client configuration is discussed in detail in BocaConfiguration; for now, understand that webserviceclient.properties tells the client to connect to a Boca server over a web service on a particular host and port and with a particular user name and password. Though irrelevant to this example, it also contains JMS login information for receiving updates

The named graph is the most atomic unit of storage in Boca. A named graph is a collection of triples named by a URI. If a triple exists in more than one named graph, each such triple is considered to be independent. Data access in Boca comprises several variations on the following basic theme: ask for a Model interface to a named graph, perform zero or more reads, execute zero or more transactions, and close the model. (25-27) show the most basic way to acquire such an interface. The NamedGraphModel is an enhanced Jena Model with some extra abilities, but for now we can just treat it as a regular old Model. The getRemoteModel call itself retrieves a Model that performs all transactions and reads directly against the Boca store, regardless of whether we use a Web Service or embedded client. The remote model is a good way to begin porting existing Jena applications to Boca. The second argument, createIfNecessary, tells Boca to allocate a new named graph for the given URI if one does not already exist. If this parameter were false, the call would return null if the named graph did not yet exist.

(28-39) add two statements to our NamedGraphModel in a single transaction. Placing operations on one's Jena Model into transactions presents the greatest challenging when moving to Boca. If no transaction boundaries are specified then every add and remove is performed in a separate transaction, and is very expensive. This example is simple, but it is very important to abort a transaction should the code enter an exception state before committing. Otherwise, the transaction will remain open and will be continued by other transactions elsewhere in the code.

(41) is the most cryptic looking line in the code. We have added statements to the model, committed our transaction, so why do we have to replicate? For that matter, what is replication? The answer is that committing a transaction merely ends the current transaction in the transaction queue. Subsequent begin-commit blocks would queue up more transactions. When replication is invoked, as in (41) all committed transactions are sent, in order, to Boca's database layer (either via a Web Service call or directly via method calls, depending on configuration) where conflict resolution and database operations occur. In general, replication sends all outgoing changes to the database and brings down any new and pertinent data. Since the remote models--as in this example--have no local storage component, no data is retrieved when a remote model is replicated. In this example, replication is performed manually. Boca additionally supports modes in which replication is automatic and/or occurs in the background. Replication is covered in more depth in BocaConcepts.

What would happen if we were to read from our NamedGraphModel before replication (41)? For example, what if we ask for all objects of the resource "http://example.org/res1"? We'd like our model to reflect not only what's on the server, but rather all committed transactions as well as the current transaction. The Boca API does exactly that. All reads to remote models are proxied through the transaction queue so that updated information from the perspective of the client is reflected. In fact, all models created with the same DatasetService share a transaction queue. Thus, all models across the application will see the correct state. However, only the owning thread of the current transaction will see updates to the uncommitted transaction.

We conclude this example with a thrilling discussion of the resource management code, i.e. try-catch and try-finally blocks and close()</close> calls. Each <code>DatasetService manages all instances of remote and local (see next section) models. In particular, it issues only a single object of each type of model per named graph URI. Thus, two threads or application components may share the same instance of NamedGraphModel. The close() call on line (44) tells the DatasetService that it is done with the resource. If an application does not release model resources, it will eventually run out of memory. The close() call of the DatasetService itself is less critical since these umbrella objects typically last throughout the application lifetime. Most code that interacts with Boca will not instantiate or close DatasetService objects. This will typically be handled by single points of setup and tear-down code, once in the application.

The adventurous Semantic Web developer will assuredly be off writing their own Boca examples by now, but we encourage you to at least peruse the remainder of the examples to get a more complete understanding of the features Boca provides.

Example 2: The Local Model: maintaining triples on the client


(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.rdf.model.Property;
(7)   import com.hp.hpl.jena.rdf.model.Resource;
(8)   import com.hp.hpl.jena.rdf.model.StmtIterator;
(9)   import com.ibm.adtech.boca.client.DatasetService;
(10)  import com.ibm.adtech.boca.client.NamedGraphModel;
(11)  
(12)  public class LocalModelWebService {
(13)      
(14)      public static void main(String[] args) throws Exception {
(15)      
(16)          DatasetService datasetService1 = null;
(17)          DatasetService datasetService2 = null;
(18)          
(19)          try {
(20)          
(21)              Properties webserviceProperties = new Properties();
(22)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(23)              
(24)              datasetService1 = new DatasetService(webserviceProperties);
(25)              datasetService1.getNotificationService().setUsingNotification(false);
(26)              
(27)              datasetService2 = new DatasetService(webserviceProperties);    
(28)              
(29)              String namedGraphURI = "http://example.org/ng1";
(30)              boolean createIfNecessary = true;
(31)              NamedGraphModel localModel = null;
(32)              NamedGraphModel remoteModel = null;
(33)              try {
(34)                  
(35)                  remoteModel = datasetService2.getRemoteModel(namedGraphURI, createIfNecessary);
(36)                  
(37)                  Resource res1 = remoteModel.createResource("http://example.org/res1");
(38)                  Property prop1 = remoteModel.createProperty("http://example.org/prop1");
(39)                  remoteModel.begin();
(40)                  try {
(41)                      remoteModel.add(res1, prop1, "value1");
(42)                      remoteModel.add(res1, prop1, "value2");
(43)                      remoteModel.commit();
(44)                  } catch (Exception e) {
(45)                      remoteModel.abort();
(46)                      throw e;
(47)                  }
(48)                  
(49)                  datasetService2.getDatasetReplicator().replicate(true);
(50)                  
(51)                  
(52)                  localModel = datasetService1.getLocalModel(namedGraphURI, createIfNecessary, true);
(53)                  
(54)                  StmtIterator itr = localModel.listStatements();
(55)                  System.err.println("Statments before replication (should empty):");
(56)                  while (itr.hasNext()) {
(57)                      System.err.println(itr.nextStatement());
(58)                  }
(59)                  datasetService1.getDatasetReplicator().replicate(true);
(60)                  
(61)                  itr = localModel.listStatements();
(62)                  System.err.println("Statments after replication:");
(63)                  while (itr.hasNext()) {
(64)                      System.err.println(itr.nextStatement());
(65)                  }
(66)              } finally {
(67)                  try {
(68)                      if (remoteModel != null)
(69)                          remoteModel.close();
(70)                  } finally {
(71)                      if (localModel != null)
(72)                          localModel.close();
(73)                  }
(74)              }
(75)          } finally {
(76)              try {
(77)                  if (datasetService1 != null)
(78)                      datasetService1.close();
(79)              } finally {
(80)                  if (datasetService2 != null)
(81)                      datasetService2.close();
(82)              }
(83)          }
(84)      }
(85)  
(86)  }

Even though many unreplicated or uncommitted triples may reside in and be read from the transaction queue, all reads made on a remote model must go to the database. With a Web Service client, this means network calls. This is often not a feasible design for RDF applications (which are often already slower than traditional applications given the complexity of RDF manipulation in a database or even in memory). Boca provides local storage to alleviate this problem. A local model is a memory- or local-database-backed model that Boca synchronizes with a particular subset of the server database. For a complete description of local models see BocaConcepts. Simply put, the user may specify interest in an entire named graph, a subset of all triples in the database (using triples patterns), or a subset of a particular named graph (using triple patterns). The Boca objects that handle subscriptions are called trackers. A named graph tracker tracks an entire graph, while a selector tracker tracks a triple pattern. When a DatasetService is replicated, all committed transactions on the local as well as remote models are pushed to the server. In addition, all changes to the database matching a client's subscriptions are brought down to the local model and now may be read locally. The local model may also be updated using JMS for near real-time updates. This is covered in a later example.

When a local model receives triple updates as a result of a replication, it typically means that another application or client added or removed triples from the main database. To simulate that in our example, we will use two separate DatasetService instances. (21-27) instantiate both DatasetService objects. (35-49) load a remote model from datasetService2 and commit a transaction a la Example 1, setting the stage for the main event of Example 2.

(52) fetches a local model implementation of a NamedGraphModel from datasetService1. This method differs slightly from its remote model counterpart in the additional third argument, which tells Boca whether or not we want the named graph to be maintained in the local model via replication (and, in fact, via notification as well). If true, a named graph tracker will be registered to track changes to the named graph. So do we now have a copy of the named graph locally? Not so fast--we haven't replicated yet. (54-58) provide a sanity check that our local model is still empty. On (59) we finally replicate the DatasetService belonging to our local model, and when you run this example (61-65) will show that the local model now has the correct statements. In this example, we manually replicated the DatasetService. This call is synchronous, in that it will only return once replication is complete. Recall that Boca supports additional replication modes whereby replication occurs in a background thread at some point after committing the transaction. If we were in one of those modes, we could not count on replication completing without listening for additional events. (66-83) illustrate how resource management is slightly more complicated given multiple NamedGraphModel instances. Each call to close() requires its own try-finally block.

Example 3: Embedded Mode

We can run Example 1 using an embedded client simply by modifying just a bit of code.

(18)              Properties webserviceProperties = new Properties();
(19)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(20)  
(21)              datasetService = new DatasetService(webserviceProperties);

becomes...

(18)              Properties embeddedProperties = new Properties();
(19)              embeddedProperties.load(new FileInputStream("embedded.properties"));
(20)  
(21)              datasetService = new DatasetService(embeddedProperties);

We'll cover the details of configuring an embedded client in BocaConfiguration. At the risk of oversimplifying, the embedded.properties contains database connection properties in lieu of Web Service endpoint properties. This example runs without another Boca server.

Local model subscriptions and notification

In this set of examples, we delve a bit deeper into local model subscriptions, and we will show how notification works.

Example 4: The Selector Tracker

(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.rdf.model.Property;
(7)   import com.hp.hpl.jena.rdf.model.Resource;
(8)   import com.hp.hpl.jena.rdf.model.StmtIterator;
(9)   import com.ibm.adtech.boca.client.DatasetService;
(10)  import com.ibm.adtech.boca.client.NamedGraphModel;
(11)  import com.ibm.adtech.boca.services.trackers.SelectorTracker;
(12)  
(13)  public class LocalModelSelectorTracker {
(14)      
(15)      public static void main(String[] args) throws Exception {
(16)          
(17)          DatasetService datasetService1 = null;
(18)          DatasetService datasetService2 = null;
(19)          
(20)          try {
(21)              Properties webserviceProperties = new Properties();
(22)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(23)              
(24)              datasetService1 = new DatasetService(webserviceProperties);
(25)              datasetService1.getNotificationService().setUsingNotification(false);
(26)              
(27)              datasetService2 = new DatasetService(webserviceProperties);    
(28)              
(29)              String namedGraphURI = "http://example.org/ng1";
(30)              boolean createIfNecessary = true;
(31)              NamedGraphModel localModel = null;
(32)              NamedGraphModel remoteModel = null;
(33)              try {
(34)                  
(35)                  remoteModel = datasetService2.getRemoteModel(namedGraphURI, createIfNecessary);
(36)                  
(37)                  Resource res1 = remoteModel.createResource("http://example.org/res1");
(38)                  Property prop1 = remoteModel.createProperty("http://example.org/prop1");
(39)                  remoteModel.begin();
(40)                  try {
(41)                      remoteModel.add(res1, prop1, "value1");
(42)                      remoteModel.add(res1, prop1, "value2");
(43)                      remoteModel.commit();
(44)                  } catch (Exception e) {
(45)                      remoteModel.abort();
(46)                      throw e;
(47)                  }
(48)                  
(49)                  datasetService2.getDatasetReplicator().replicate(true);
(50)                  
(51)                  
(52)                  localModel = datasetService1.getLocalModel(namedGraphURI, createIfNecessary, false);
(53)                  SelectorTracker tracker = new SelectorTracker(null, null, null, localModel.createLiteral("value1").asNode());
(54)                  datasetService1.getDatasetReplicator().addTracker("http://example.org#testSet", tracker);
(55)                  
(56)                  StmtIterator itr = localModel.listStatements();
(57)                  System.err.println("Statments before replication (should empty):");
(58)                  while (itr.hasNext()) {
(59)                      System.err.println(itr.nextStatement());
(60)                  }
(61)                  datasetService1.getDatasetReplicator().replicate(true);
(62)                  
(63)                  itr = localModel.listStatements();
(64)                  System.err.println("Statments after replication, should only be 'value1'");
(65)                  while (itr.hasNext()) {
(66)                      System.err.println(itr.nextStatement());
(67)                  }
(68)              } finally {
(69)                  try {
(70)                      if (remoteModel != null)
(71)                          remoteModel.close();
(72)                  } finally {
(73)                      if (localModel != null)
(74)                          localModel.close();
(75)                  }
(76)              }
(77)          } finally {
(78)              try {
(79)                  if (datasetService1 != null)
(80)                      datasetService1.close();
(81)              } finally {
(82)                  if (datasetService2 != null)
(83)                      datasetService2.close();
(84)              }
(85)          }
(86)      }
(87)  
(88)  }

In this example we show how to track a subset of triples in the database determined by a triple pattern. A triple pattern is a triple with one or more of the subject, predicate or object left blank. For example ("http://example.org/res1","http://example.org/res1",*) refers to the set of all triples with the given subject and predicate. Through (49) this example greatly resembles Example 2. We instantiate two DatasetService objects, and use a remote model in one of them to add some statements to a named graph. Then we replicate that DatasetService to send the statements to the server.

The first divergence from Example 2 comes at (52). We pass in false for the third argument because we do not want to track all statements in the named graph, only a subset that we will specify now. (53) creates a SelectorTracker object. The first argument specifies that we are interested triples from any named graph that match the pattern. In reality, because we only have a local model for one named graph, matching triples from other named graphs will be ignored during replication. The remaining three arguments define the triple pattern itself. These must be instances of Jena Node objects, or null to indicate a wildcard element. In this example, we have elected to track all triples that have the plain literal "value1" as their object. Now that the tracker has been created, we must add it to the replication system. (54) adds the tracker to the dataset replicator. Notice. how the tracker is not added to the local model NamedGraphModel itself. Replication and tracking is handled at the DatasetService level. More on this in BocaConcepts. The first argument of addTracker() defines a named set of trackers to which the new tracker is to be added. Tracker sets are useful for managing groups of trackers, but can be ignored for now.

The remainder of the example follows in the vein of Example 2. We perform a sanity check to make sure the model is empty before replicating the local model's DatasetService, then replicate, and then make sure triples were brought down. In this case, the named graph model contains triples with objects "value1" and "value2". However, since our tracker is programmed to only track statements with object "value1", we should see only a single triple printed.

Example 5: Notification: receiving updates to the local model without replication


(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.rdf.listeners.StatementListener;
(7)   import com.hp.hpl.jena.rdf.model.Property;
(8)   import com.hp.hpl.jena.rdf.model.Resource;
(9)   import com.hp.hpl.jena.rdf.model.Statement;
(10)  import com.ibm.adtech.boca.client.DatasetService;
(11)  import com.ibm.adtech.boca.client.NamedGraphModel;
(12)  import com.ibm.adtech.boca.services.trackers.SelectorTracker;
(13)  
(14)  public class LocalModelNotification {
(15)  
(16)      public static void main(String[] args) throws Exception {
(17)  
(18)          DatasetService datasetService1 = null;
(19)          DatasetService datasetService2 = null;
(20)          try {
(21)              Properties webserviceProperties = new Properties();
(22)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(23)  
(24)              datasetService1 = new DatasetService(webserviceProperties);
(25)              datasetService1.getNotificationService().setUsingNotification(true);
(26)  
(27)              datasetService2 = new DatasetService(webserviceProperties);
(28)  
(29)              String namedGraphURI = "http://example.org/ng1";
(30)              boolean createIfNecessary = true;
(31)              NamedGraphModel localModel = null;
(32)              NamedGraphModel remoteModel = null;
(33)              try {
(34)  
(35)                  localModel = datasetService1.getLocalModel(namedGraphURI, createIfNecessary, false);
(36)                  Resource res1 = localModel.createResource("http://example.org/res1");
(37)                  Property prop1 = localModel.createProperty("http://example.org/prop1");
(38)                  datasetService1.getDatasetReplicator().replicate(true);
(39)                  SelectorTracker tracker = new SelectorTracker(null, res1.asNode(), null, null);
(40)                  datasetService1.getDatasetReplicator().addTracker("http://example.org#testSet", tracker);
(41)  
(42)                  localModel.register(new StatementListener() {
(43)                      public void addedStatement(Statement s) {
(44)                          System.err.println("addedStatement: " + s);
(45)                      }
(46)                  });
(47)  
(48)                  remoteModel = datasetService2.getRemoteModel(namedGraphURI, createIfNecessary);
(49)  
(50)                  remoteModel.begin();
(51)                  try {
(52)                      String rand = String.valueOf(Math.random());
(53)                      remoteModel.add(res1, prop1, "value1" + rand);
(54)                      remoteModel.add(res1, prop1, "value2" + rand);
(55)                      remoteModel.commit();
(56)                  } catch (Exception e) {
(57)                      remoteModel.abort();
(58)                      throw e;
(59)                  }
(60)  
(61)                  datasetService2.getDatasetReplicator().replicate(true);
(62)  
(63)                  Thread.sleep(300);
(64)              } finally {
(65)                  try {
(66)                      if (remoteModel != null)
(67)                          remoteModel.close();
(68)                  } finally {
(69)                      if (localModel != null)
(70)                          localModel.close();
(71)                  }
(72)              }
(73)          } finally {
(74)              try {
(75)                  if (datasetService1 != null)
(76)                      datasetService1.close();
(77)              } finally {
(78)                  if (datasetService2 != null)
(79)                      datasetService2.close();
(80)              }
(81)          }
(82)      }
(83)  
(84)  }

This example shows the most basic way to use the Boca notification system. We again setup two DatasetService objects to simulate two different clients. We then setup a tracker and a model listener on one DatasetService, preparing it to receive notification about triples it cares about. We then add such triples to a remote model in the other other DatasetService and watch the updates happen.

(21-32) setup the dataset services. (25) is the new and important bit here. We are telling the Boca client that we want to be notified of changes to relevant named graphs and triples in the database. Underneath, the Boca client will connect to the JMS broker specified in webserviceclient.properties (see BocaConfiguration) and register the proper subscriptions. (35) creates a local model, and we choose not to track the entire named graph. (38) is somewhat of a peculiarity. We haven't added anything to our named graph and it is presumably a new one, so why replicate? The reason being that there is always the chance in a distributed RDF system that somebody has already added statements to a particular named graph. Thus, it is good practice to replicate local models upon creation.

(39-40) registers a tracker. This time, we are tracking all statements with a particular subject. Because we have enabled notification, tracker registration also tells the notification system of our new interest so that we may receive updates to triples subsumed by our tracker. (42-46) registers a simple listener with the local model to print out any new statements as they arrive. Experienced Jena users will note that this is precisely the Jena model listener API. Thus, all existing application components--UI widgets for example--that are coded against Jena model events can react to Boca model events as well.

The remainder of this example uses the secondary DatasetService to create a remote model and pump some statements into the named graph on the server to trigger the event system. If all goes well, both statements should trigger a print out on the event listener above.

Querying Boca

The Boca API supports two query interfaces for issuing SPARQL queries. The first is the traditional Jena ARQ query interface. By using Jena's ARQ library, applications can query the remote and local models using their existing code. This works well when queries go against a local model that is either memory backed or persisted in a local database. However, querying remote models with ARQ requires a great deal of network activity and, as a result, performs poorly. Thus, we have implemented a Boca native query interface that makes use of our own SPARQL query engine called Glitter. A full description of the Boca querying subsystem as well as a better overview of using SPARQL in application development can be found in BocaQuery. For now, we provide code examples of using each interface. The query API is included as part of the Boca Model Service API, an API with a specific set of operations against a Boca server outside the scope of a particular local or remote model. In embedded mode, the model service is backed by straight calls to the database layer, while in Web Service mode..well, you get it.

In Boca, all statements are associated with a containing named graph. When making a SPARQL query, you typically have to specify default graphs and graphs. If you would like to query across all of the statements in Boca (rather than limiting yourself to enumerated named graphs), use the following URIs:

BocaQuery has more information on these URIs.

Example 6: Querying Boca with ARQ

In this example we show how to use ARQ to query Boca. For simplicity--and to show how the two query APIs can be used to accomplish the same task--we query a remote model. In practice this is too inefficient to be feasible. As we have for many of the examples, we first provide high level summary before going through line by line. We first instantiate a DatasetService using the Web Service client configuration. This example could work just as well with the embedded client configuration. We then create a remote model and add some properties to it in a transaction. After committing the transaction and replicating, we query the remote model for the statements we expect.


(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.query.QueryExecution;
(7)   import com.hp.hpl.jena.query.QueryExecutionFactory;
(8)   import com.hp.hpl.jena.query.QuerySolution;
(9)   import com.hp.hpl.jena.query.ResultSet;
(10)  import com.hp.hpl.jena.rdf.model.Property;
(11)  import com.hp.hpl.jena.rdf.model.RDFNode;
(12)  import com.hp.hpl.jena.rdf.model.Resource;
(13)  import com.hp.hpl.jena.rdf.model.ResourceFactory;
(14)  import com.hp.hpl.jena.rdf.model.impl.StatementImpl;
(15)  import com.ibm.adtech.boca.client.DatasetService;
(16)  import com.ibm.adtech.boca.client.NamedGraphModel;
(17)  
(18)  public class QueryRemoteModel {
(19)  
(20)      public static void main(String[] args) throws Exception {
(21)  
(22)          DatasetService datasetService = null;
(23)          try {
(24)              Properties webserviceProperties = new Properties();
(25)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(26)  
(27)              datasetService = new DatasetService(webserviceProperties);
(28)  
(29)              String namedGraphURI = "http://example.org/ng1";
(30)              boolean createIfNecessary = true;
(31)              NamedGraphModel remoteModel = null;
(32)              try {
(33)  
(34)                  remoteModel = datasetService.getRemoteModel(namedGraphURI, createIfNecessary);
(35)  
(36)                  Resource res1 = remoteModel.createResource("http://example.org/res1");
(37)                  Property prop1 = remoteModel.createProperty("http://example.org/prop1");
(38)                  remoteModel.begin();
(39)                  try {
(40)                      remoteModel.add(res1, prop1, "value1");
(41)                      remoteModel.add(res1, prop1, "value2");
(42)                      remoteModel.commit();
(43)                  } catch (Exception e) {
(44)                      remoteModel.abort();
(45)                      throw e;
(46)                  }
(47)  
(48)                  datasetService.getDatasetReplicator().replicate(true);
(49)  
(50)  
(51)                  String query = "SELECT ?s ?p ?o  WHERE { ?s ?p ?o }";
(52)                  QueryExecution qe = QueryExecutionFactory.create(query, remoteModel);
(53)                  for (ResultSet results = qe.execSelect(); results.hasNext();) {
(54)                      QuerySolution sol = results.nextSolution();
(55)                      Resource s = sol.getResource("s");
(56)                      Property p = ResourceFactory.createProperty(sol.getResource("p").getURI());
(57)                      RDFNode o = sol.get("o");
(58)                      System.err.println(new StatementImpl(s, p, o));
(59)                  }
(60)              } finally {
(61)                  if (remoteModel != null)
(62)                      remoteModel.close();
(63)              }
(64)          } finally {
(65)              if (datasetService != null)
(66)                  datasetService.close();
(67)          }
(68)      }
(69)  
(70)  }

(22-46) set things up in the usual fashion. The replication on (48) is not necessary for the proper query results to be produced since all graph reads go through the transaction queue. However, it does flush the transaction queue so that the triples will be found in the Boca database itself, not the queue, thus providing a more honest example. (51-59) use Jena's ARQ to query the remote model with a simple SPARQL query, that asks for all statements. The Model is only one query source in ARQ. The Dataset is another. To retrieve an ARQ compatible Dataset from the Boca client API, simply do:

   Dataset dataset = datasetService.getCurrentDataset();

Example 7: Querying Boca with the model service API

This example goes much like the previous. We populate a named graph on the server using a remote model, and then query the named graphs with SPARQL. However, we use a Boca specific query API that sends a SPARQL query around the Jena API layers and directly to our database layer. In Web Service mode, the query is send over a Web Service and the complete results come back in the response. In embedded mode, the query arrives at the database layer via straight method calls.

(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.HashSet;
(5)   import java.util.Properties;
(6)   
(7)   import com.hp.hpl.jena.graph.Graph;
(8)   import com.hp.hpl.jena.query.QuerySolution;
(9)   import com.hp.hpl.jena.query.resultset.RDFInput;
(10)  import com.hp.hpl.jena.rdf.model.ModelFactory;
(11)  import com.hp.hpl.jena.rdf.model.Property;
(12)  import com.hp.hpl.jena.rdf.model.RDFNode;
(13)  import com.hp.hpl.jena.rdf.model.Resource;
(14)  import com.hp.hpl.jena.rdf.model.ResourceFactory;
(15)  import com.hp.hpl.jena.rdf.model.impl.StatementImpl;
(16)  import com.ibm.adtech.boca.client.DatasetService;
(17)  import com.ibm.adtech.boca.client.NamedGraphModel;
(18)  
(19)  public class QueryWebService {
(20)  
(21)      public static void main(String[] args) throws Exception {
(22)  
(23)          DatasetService datasetService = null;
(24)          try {
(25)  
(26)              Properties webserviceProperties = new Properties();
(27)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(28)  
(29)              datasetService = new DatasetService(webserviceProperties);
(30)  
(31)              String namedGraphURI = "http://example.org/ng1";
(32)              boolean createIfNecessary = true;
(33)              NamedGraphModel remoteModel = null;
(34)              try {
(35)  
(36)                  remoteModel = datasetService.getRemoteModel(namedGraphURI, createIfNecessary);
(37)  
(38)                  Resource res1 = remoteModel.createResource("http://example.org/res1");
(39)                  Property prop1 = remoteModel.createProperty("http://example.org/prop1");
(40)                  remoteModel.begin();
(41)                  try {
(42)                      remoteModel.add(res1, prop1, "value1");
(43)                      remoteModel.add(res1, prop1, "value2");
(44)                      remoteModel.commit();
(45)                  } catch (Exception e) {
(46)                      remoteModel.abort();
(47)                      throw e;
(48)                  }
(49)  
(50)                  datasetService.getDatasetReplicator().replicate(true);
(51)  
(52)  
(53)                  String query = "SELECT ?s ?p ?o  WHERE { ?s ?p ?o }";
(54)                  HashSet<String> defaultGraphs = new HashSet<String>();
(55)                  defaultGraphs.add(namedGraphURI);
(56)                  Graph results = datasetService.getModelService().execQuery(defaultGraphs, new HashSet<String>(), query,
 "http://jena.hpl.hp.com/2003/07/query/SPARQL");
(57)                  RDFInput rdfi = new RDFInput(ModelFactory.createModelForGraph(results));
(58)                  while (rdfi.hasNext()) {
(59)                      QuerySolution qs = rdfi.nextSolution();
(60)                      RDFNode s = qs.get("s");
(61)                      RDFNode p = qs.get("p");
(62)                      RDFNode o = qs.get("o");
(63)                      System.err.println(new StatementImpl(ResourceFactory.createResource(s.toString()),
 ResourceFactory.createProperty(p.toString()), ResourceFactory.createResource(o.toString())));
(64)                  }
(65)              } finally {
(66)                  if (remoteModel != null)
(67)                      remoteModel.close();
(68)              }
(69)          } finally {
(70)              if (datasetService != null)
(71)                  datasetService.close();
(72)          }
(73)      }
(74)  
(75)  }

(23-48) is business as usual, setting things up. (50) performs a necessary replication. Unlike the Jena based queries, the model service API queries go directly to the server, bypassing the transaction queue. Thus, if we did not replicate, the query below would not return any results.

(53-56) prepares and executes the query. Those not familiar with SPARQL (and even those who are) might be confused by the first two arguments to execQuery(). The first is the set of named graphs whose union defines the default graph for the SPARQL query. In a SPARQL query, the default graph is queried for all query components that are not within a GRAPH clause. The second argument is a set of named graphs that may be referenced in GRAPH clauses in the query. BocaQuery discuss these points in greater detail. The third argument is the query text itself and the fourth is the query language. Currently, only "http://jena.hpl.hp.com/2003/07/query/SPARQL" is supported. The execQuery method returns the result in a Graph that we convert to an RDFInput with which we can iterate through the results.

More advanced Boca features: commands, preconditions and versioning

The next trio of examples demonstrate some of the more advanced features of Boca. First, we introduce the Boca command framework, a method for encapsulating complex RDF updates as atomic operations to be executed in transactions. Next, we show how to lookup the current revision of a named graph as well as retrieve a specific revision. Finally, we show how to set a precondition for command execution using a query for a named graph revision.

Example 8: The Boca command execution framework

RDF-based applications are often composed of logically-related sets of statement additions and deletions. The Boca command framework allows for the concrete componentization of such operations to facilitate modularization and reuse. A command is simply an executable piece of code, not terribly unlike a standard Java Thread class. This example shows how to implement two simple commands and execute them in a single transaction. The command chain allows multiple commands to be executed in the same transaction. It also provides a mechanism for passing the results of one command to the next command in the chain.

(1)   
(2)   package com.ibm.adtech.boca.sample;
(3)   
(4)   import java.io.FileInputStream;
(5)   import java.util.Properties;
(6)   
(7)   import com.hp.hpl.jena.rdf.model.ResourceFactory;
(8)   import com.hp.hpl.jena.rdf.model.Statement;
(9)   import com.hp.hpl.jena.rdf.model.impl.StatementImpl;
(10)  import com.ibm.adtech.boca.client.DatasetService;
(11)  import com.ibm.adtech.boca.client.NamedGraphModel;
(12)  import com.ibm.adtech.boca.commands.XCommand;
(13)  import com.ibm.adtech.boca.commands.XCommandChain;
(14)  import com.ibm.adtech.boca.commands.XCommandImpl;
(15)  import com.ibm.adtech.boca.services.IReplicationService;
(16)  
(17)  public class CommandLinking {
(18)  
(19)      public static void main(String[] args) throws Exception {
(20)  
(21)          DatasetService datasetService = null;
(22)          try {
(23)              Properties webserviceProperties = new Properties();
(24)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(25)  
(26)              datasetService = new DatasetService(webserviceProperties);
(27)              datasetService.getDatasetReplicator().setReplicationMode(IReplicationService.REPLICATION_MANUAL);
(28)              
(29)              String namedGraphURI = "http://example.org/ng1";
(30)              boolean createIfNecessary = true;
(31)              boolean addNamedGraphTracker = true;
(32)  
(33)              final NamedGraphModel localModel = datasetService.getLocalModel(namedGraphURI, createIfNecessary, addNamedGraphTracker);
(34)  
(35)              datasetService.getDatasetReplicator().replicate(true);
(36)  
(37)              final String inputProperty = "input";
(38)              final Statement stmt = new StatementImpl(ResourceFactory.createResource("http://res1"), ResourceFactory.createProperty("http://test.ibm.com/test#property"), ResourceFactory.createResource("http://res1"));
(39)  
(40)              XCommand addStmtCmd = new XCommandImpl() {
(41)  
(42)                  public Object execute() {
(43)                      System.out.println("> executing 'addStmtCmd'");
(44)                      System.out.println("  > adding:    " + stmt);
(45)                      localModel.add(stmt);
(46)                      System.out.println("  > returning: "+stmt);
(47)                      return stmt;
(48)                  }
(49)              };
(50)  
(51)              XCommand removeStmtCmd = new XCommandImpl() {
(52)  
(53)                  public Object execute() {
(54)                      System.out.println("> executing 'removeStmtCmd'");
(55)                      Statement resultFromPreviousCmd = (Statement) getInputProperty(inputProperty);
(56)                      System.out.println("  > getting input: " + resultFromPreviousCmd);
(57)                      System.out.println("  > removing:      " + resultFromPreviousCmd);
(58)                      localModel.remove(resultFromPreviousCmd);
(59)                      System.out.println("  > returning");
(60)                      return null;
(61)                  }
(62)              };
(63)  
(64)              XCommandChain chain = new XCommandChain();
(65)              chain.addCommand(addStmtCmd);
(66)              chain.addCommand(removeStmtCmd);
(67)  
(68)              System.out.println("> Linking output of 'addStmtCmd' to the input of 'removeStmtCmd");
(69)              chain.linkCommand(removeStmtCmd, inputProperty, addStmtCmd);
(70)  
(71)              datasetService.getTransactionQueueHandler().executeCommandsInTransaction(chain);
(72)  
(73)              if (localModel != null)
(74)                  localModel.close();
(75)  
(76)          } finally {
(77)              if (datasetService != null)
(78)                  datasetService.close();
(79)          }
(80)      }
(81)  
(82)  }

(23-35) setup a DatasetService and replicate it to make sure we are up to date if the named graph already exists. (27) is not necessary but demonstrates how to set the replication mode. (MANUAL is the default value.) On (33), we declare our local NamedGraphModel as final because we will need to reference it from within an inner method. Let's get to the good stuff.

(37) defines an input property String constant. Input properties are named properties that are used to pass data between commands in a chain. (40-49) defines our first command. The execute() method may do anything it likes. However, all RDF operations must be performed on a NamedGraphModel owned by the DatasetService that will execute the command. In this simple command, we add a statement to our local model. The second XCommand (51-62) is a bit more complex. (55) retrieves the value of a named input property. The command implementation doesn't have to worry about where the value comes from, it need only know the name of the property. In this case we are expecting the value of the input property to be a Jena Statement. (64-69) setup the command chain. Command are executed in the order in which they are added to the chain. (69) link the commands together. The first argument is the target command, and the second is the named input property of the command to use. The third argument is the command whose return value can be used. In longer command chains, this can refer to any upstream command in the chain. (71) executes the command.

Example 9: Boca named graph versioning

When a Boca named graph is instantiated, it becomes revision 1. Every time an individual transaction that touches a Boca named graph is committed, the version of that named graph is incremented. If multiple transactions are committed in a single transaction, the revision is incremented once for each. In this example we introduce the metadata graph concept. The metadata graph is a read-only graph containing system metadata about a named graph, including such information as version and access control. For more, see BocaSecurity.

Here, we'll show how to query version info from the metadata graph, as well as how to retrieve a snap-shot of a particular revision of a named graph. Since writing to a particular revision of a named graph has undefined semantics, these snap shots are read-only. If a user wishes to roll back to particular revision, the correct procedure is to load a snapshot of that version, remove all the statements from a live local or remote model, and commit. The revision number of the named graph will of course increment, as revision numbers are monotonic, but the contents of the graph will be rolled back.


(1)   
(2)   package com.ibm.adtech.boca.sample;
(3)   
(4)   import java.io.FileInputStream;
(5)   import java.util.Properties;
(6)   
(7)   import com.hp.hpl.jena.graph.Node;
(8)   import com.hp.hpl.jena.graph.Triple;
(9)   import com.hp.hpl.jena.rdf.model.Model;
(10)  import com.hp.hpl.jena.rdf.model.ModelFactory;
(11)  import com.hp.hpl.jena.rdf.model.ResourceFactory;
(12)  import com.hp.hpl.jena.rdf.model.Statement;
(13)  import com.hp.hpl.jena.rdf.model.StmtIterator;
(14)  import com.hp.hpl.jena.rdf.model.impl.StatementImpl;
(15)  import com.hp.hpl.jena.util.iterator.ExtendedIterator;
(16)  import com.ibm.adtech.boca.client.DatasetService;
(17)  import com.ibm.adtech.boca.client.NamedGraphModel;
(18)  import com.ibm.adtech.boca.models.INamedGraphWithMetaData;
(19)  import com.ibm.adtech.boca.rdf.repository.BOCAFactory;
(20)  import com.ibm.adtech.boca.rdf.repository.NamedGraph;
(21)  import com.ibm.adtech.boca.services.IReplicationService;
(22)  
(23)  public class GraphRevision {
(24)  
(25)      public static void main(String[] args) throws Exception {
(26)  
(27)          final Statement stmt1 = new StatementImpl(ResourceFactory.createResource("http://subject1"), ResourceFactory.createProperty("http://test.ibm.com/test#property"), ResourceFactory.createResource("http://object1"));
(28)          final Statement stmt2 = new StatementImpl(ResourceFactory.createResource("http://subject2"), ResourceFactory.createProperty("http://test.ibm.com/test#property"), ResourceFactory.createResource("http://object2"));
(29)  
(30)          DatasetService datasetService = null;
(31)          try {
(32)  
(33)              Properties webserviceProperties = new Properties();
(34)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(35)  
(36)              datasetService = new DatasetService(webserviceProperties);
(37)              datasetService.getDatasetReplicator().setReplicationMode(IReplicationService.REPLICATION_IMMEDIATE);
(38)  
(39)              final String NAMED_GRAPH_URI = "http://example.org/ng1" + Math.random();
(40)              boolean createIfNecessary = true;
(41)              boolean addNamedGraphTracker = true;
(42)              NamedGraphModel localModel1 = null;
(43)              
(44)              try {
(45)  
(46)                  localModel1 = datasetService.getLocalModel(NAMED_GRAPH_URI, createIfNecessary, addNamedGraphTracker);
(47)  
(48)  
(49)                  long currentRevision = getNamedGraphRevision(datasetService.getModelService().getCurrentNamedGraphRevision(Node.createURI(NAMED_GRAPH_URI)));
(50)                  System.out.println("> Initial revision for the named graph: " + currentRevision);
(51)                  System.out.println("> adding: " + stmt1);
(52)                  localModel1.add(stmt1);
(53)  
(54)                  currentRevision = getNamedGraphRevision(datasetService.getModelService().getCurrentNamedGraphRevision(Node.createURI(NAMED_GRAPH_URI)));
(55)                  System.out.println("> Current revision for the named graph: " + currentRevision);
(56)                  System.out.println("> adding: " + stmt2);
(57)                  localModel1.add(stmt2);
(58)  
(59)                  currentRevision = getNamedGraphRevision(datasetService.getModelService().getCurrentNamedGraphRevision(Node.createURI(NAMED_GRAPH_URI)));
(60)                  System.out.println("> Current revision for the named graph: " + currentRevision);
(61)                  System.out.println("> removing: " + stmt2);
(62)                  localModel1.remove(stmt2);
(63)  
(64)                  currentRevision = getNamedGraphRevision(datasetService.getModelService().getCurrentNamedGraphRevision(Node.createURI(NAMED_GRAPH_URI)));
(65)                  System.out.println("> Current revision for the named graph: " + currentRevision);
(66)  
(67)  
(68)                  INamedGraphWithMetaData localGraph1V2 = datasetService.getModelService().getNamedGraphRevision(Node.createURI(NAMED_GRAPH_URI), 2);
(69)                  Model localModel1V2 = ModelFactory.createModelForGraph(localGraph1V2);
(70)  
(71)                  System.out.println("> Named graph revision 2 contains: ");
(72)                  for (StmtIterator iter = localModel1V2.listStatements(); iter.hasNext();)
(73)                      System.out.println("  - " + iter.next());
(74)  
(75)              } finally {
(76)                  if (localModel1 != null)
(77)                      localModel1.close();
(78)              }
(79)          } finally {
(80)              if (datasetService != null)
(81)                  datasetService.close();
(82)          }
(83)      }
(84)  
(85)      static long getNamedGraphRevision(INamedGraphWithMetaData graph) {
(86)          NamedGraph ng = BOCAFactory.getNamedGraph(graph.getNamedGraphUri(), graph.getMetaDataGraph());
(87)          return ng.getRevision();
(88)      }
(89)  
(90)  }

(27-28) setup two statements that we use. (30-46) setup the DatasetService and instantiate a local model NamedGraphModel. On (37) we set the replication mode to IMMEDIATE so that a replication will be kicked off in the background upon any operation, such as a transaction commit or creation of a named graph as in (46). Even though the replication occurs in the background, it completes in time for the next DatasetService call. For example, the replication completes before we query the model service for the named graph revision. (49) has quite a bit going on.

   datasetService.getModelService().getCurrentNamedGraphRevision(Node.createURI(NAMED_GRAPH_URI))

asks the model service API for a read-only, abstract interface to the named graph. This call is necessary to acquire a named graph instance with a serviceable metadata graph. The metadata graph from the local model or remote model's underlying named graph is also suitable for this operation. We skip ahead to the implementation of getNamedGraphRevision (85-87). (86) instantiates a Jastor bean around the metadata graph, with our named graph URI as the object URI. This is actually using a slightly modified version of Jastor, adapted to the Jena graph API. The generated NamedGraph interface allows us to easily extract the revision number.

(50) we report the initial revision which ought to be 1. (52) we add a statement. Notice that we do not wrap this add in a begin...commit. If an operation on a model is performed outside of a transaction, that operation is assigned it's own transaction. (54) shows how the revision has been incremented due to the background replication completing. We increment the revision a few more times.

(68) shows how to retrieve a snapshot of a particular named graph revision. Notice how this is an operation on the model service itself, completely independent from any local or remote model instance. (69-73) shows how to access this graph with a Model interface and print out the statements.

This example, while simple enough code-wise, requires understanding of some of the more complex Boca principles. You are encouraged to read BocaConcepts for a more careful discussion.

Example 10: Boca command preconditions

In the final example, we combine the concepts of the previous two. We create a command, using a precondition that the named graph is at a particular revision. This is the most basic tool to solve multiple clients or threads concurrently modifying a named graph. Before modifying a named graph, a client might assert that the named graph should be at the revision at which they last read it; otherwise, it has been written by someone else since. This is a special case that precludes concurrent modification at the named-graph level. More granular locking can be achieved by asserting preconditions on particular triples in the model.


(1)   
(2)   package com.ibm.adtech.boca.sample;
(3)   
(4)   import java.io.FileInputStream;
(5)   import java.util.HashSet;
(6)   import java.util.Properties;
(7)   import java.util.Set;
(8)   
(9)   import com.hp.hpl.jena.graph.Node;
(10)  import com.hp.hpl.jena.graph.Triple;
(11)  import com.hp.hpl.jena.rdf.model.ResourceFactory;
(12)  import com.hp.hpl.jena.rdf.model.Statement;
(13)  import com.hp.hpl.jena.rdf.model.impl.StatementImpl;
(14)  import com.hp.hpl.jena.util.iterator.ExtendedIterator;
(15)  import com.ibm.adtech.boca.client.DatasetService;
(16)  import com.ibm.adtech.boca.client.NamedGraphModel;
(17)  import com.ibm.adtech.boca.commands.AskResult;
(18)  import com.ibm.adtech.boca.commands.IPrecondition;
(19)  import com.ibm.adtech.boca.commands.XCommand;
(20)  import com.ibm.adtech.boca.commands.XCommandImpl;
(21)  import com.ibm.adtech.boca.commands.XPrecondition;
(22)  import com.ibm.adtech.boca.common.exceptions.UpdateServerException;
(23)  import com.ibm.adtech.boca.models.INamedGraphWithMetaData;
(24)  import com.ibm.adtech.boca.rdf.repository.BOCAFactory;
(25)  import com.ibm.adtech.boca.rdf.repository.NamedGraph;
(26)  import com.ibm.adtech.boca.services.IReplicationService;
(27)  
(28)  public class CommandWithPrecondition {
(29)  
(30)      public static void main(String[] args) throws Exception {
(31)  
(32)          DatasetService datasetService = null;
(33)          try {
(34)              Properties webserviceProperties = new Properties();
(35)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(36)  
(37)              datasetService = new DatasetService(webserviceProperties);
(38)              datasetService.getDatasetReplicator().setReplicationMode(IReplicationService.REPLICATION_MANUAL);
(39)  
(40)              String namedGraphURI = "http://example.org/ng1";
(41)              boolean createIfNecessary = true;
(42)              boolean addNamedGraphTracker = true;
(43)  
(44)              final NamedGraphModel localModel = datasetService.getLocalModel(namedGraphURI, createIfNecessary, addNamedGraphTracker);
(45)  
(46)              datasetService.getDatasetReplicator().replicate(true);
(47)  
(48)  
(49)              long currentRevision = getNamedGraphRevision(datasetService.getModelService().getCurrentNamedGraphRevision(Node.createURI(localModel.getNamedGraphUri().getURI())));
(50)  
(51)              final Statement stmt1 = new StatementImpl(ResourceFactory.createResource("http://res1"), ResourceFactory.createProperty("http://test.ibm.com/test#property"), ResourceFactory.createResource("http://res2"));
(52)  
(53)              XCommand command1 = new XCommandImpl() {
(54)                  public Object execute() {
(55)                      localModel.add(stmt1);
(56)                      return null;
(57)                  }
(58)              };
(59)  
(60)              IPrecondition entryGraphPrecondition = new XPrecondition();
(61)  
(62)              Set<String> defaults = new HashSet<String>();
(63)              defaults.add(localModel.getNamedGraph().getMetaDataGraph().getNamedGraphUri().getURI());
(64)              entryGraphPrecondition.setDefaultGraphUris(defaults);
(65)  
(66)              entryGraphPrecondition.setQuery("ASK { <" + localModel.getNamedGraph().getNamedGraphUri().getURI() + "> <" + NamedGraph.revisionProperty + "> \"" + currentRevision + "\"^^<http://www.w3.org/2001/XMLSchema#long>}");
(67)              entryGraphPrecondition.setResult(new AskResult(true));
(68)  
(69)              command1.addPrecondition(entryGraphPrecondition);
(70)  
(71)              datasetService.getTransactionQueueHandler().executeInTransaction(command1);
(72)  
(73)              datasetService.getDatasetReplicator().replicate(true);
(74)  
(75)              if (localModel.contains(stmt1))
(76)                  System.out.println("> stmt1 exist in the local model");
(77)              else
(78)                  System.out.println("> stmt1 does NOT exist in the local model");
(79)  
(80)  
(81)              final Statement stmt2 = new StatementImpl(ResourceFactory.createResource("http://res2"), ResourceFactory.createProperty("http://test.ibm.com/test#property"), ResourceFactory.createResource("http://res2"));
(82)  
(83)              XCommand command2 = new XCommandImpl() {
(84)  
(85)                  public Object execute() {
(86)                      localModel.add(stmt2);
(87)                      return null;
(88)                  }
(89)              };
(90)  
(91)              IPrecondition entryGraphPrecondition2 = new XPrecondition();
(92)  
(93)              Set<String> defaults2 = new HashSet<String>();
(94)              defaults2.add(localModel.getNamedGraph().getMetaDataGraph().getNamedGraphUri().getURI());
(95)              entryGraphPrecondition2.setDefaultGraphUris(defaults2);
(96)  
(97)              entryGraphPrecondition2.setQuery("ASK { <" + localModel.getNamedGraph().getNamedGraphUri().getURI() + "> <" + NamedGraph.revisionProperty + "> \"" + 30 + "\"^^<http://www.w3.org/2002/XMLSchema#long>}");
(98)              entryGraphPrecondition2.setResult(new AskResult(true));
(99)  
(100)             command2.addPrecondition(entryGraphPrecondition2);
(101) 
(102)             datasetService.getTransactionQueueHandler().executeInTransaction(command2);
(103) 
(104)             try {
(105)                 datasetService.getDatasetReplicator().replicate(true);
(106)             } catch (UpdateServerException e) {
(107)                 System.out.println(">> The command was not committed to the server because the precondition failed.");
(108)             }
(109) 
(110)             if (localModel.contains(stmt2))
(111)                 System.out.println("> stmt2 exist in the local model");
(112)             else
(113)                 System.out.println("> stmt2 does NOT exist in the local model");
(114) 
(115)             if (localModel != null)
(116)                 localModel.close();
(117) 
(118)         } finally {
(119)             if (datasetService != null)
(120)                 datasetService.close();
(121)         }
(122)     }
(123) 
(124)     static long getNamedGraphRevision(INamedGraphWithMetaData graph) {
(125)         NamedGraph ng = BOCAFactory.getNamedGraph(graph.getNamedGraphUri(), graph.getMetaDataGraph());
(126)         return ng.getRevision();
(127)     }
(128) 
(129) }


(34-46) configure the DatasetService with MANUAL replication and instantiates a local model. We replicate the model to start things off. (49) captures the current revision of the named graph before we perform any operations on it. (53-58) defines a simple command. (60-69) show how to add a precondition on the command for the named graph revision. In the current version, preconditions may be specified only with SPARQL ASK queries. That is, queries that have a yes-no answer, such as "Is name graph <foo> at version 5?". As with all SPARQL queries, the ASK query in the precondition requires a set of default graphs. And because revision information is contained in the metadata graph, we create a singleton set containing the URI of the metadata graph for the named graph in question. The query itself simply checks for the existence of the triple indicating the current named graph. (71-73) execute the command and replicate. (75-78) verify that the command was succesfull.

In the remainder of the example we repeat the exercise. However, we use the same current revision that we read before we committed the first transaction. Thus, we expect this second attempt to fail because the actual revision will have been incrimented by the successful initial transaction.

Boca Administration

We now provide a couple examples of programatic administration of Boca. Boca cannot be completely managed in this form, but here are a few examples anyhow.

Example 11: Users and access controls

In this example we show how use the normal named graph model API to add new users and roles to Boca. We then show how to login with this new user and begin exercising their new permissions. A more complete treatment of the Boca access control system may be found in BocaSecurity


(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.graph.Node;
(7)   import com.hp.hpl.jena.rdf.model.Literal;
(8)   import com.hp.hpl.jena.rdf.model.Property;
(9)   import com.hp.hpl.jena.rdf.model.Resource;
(10)  import com.hp.hpl.jena.rdf.model.ResourceFactory;
(11)  import com.ibm.adtech.boca.client.DatasetService;
(12)  import com.ibm.adtech.boca.client.NamedGraphModel;
(13)  import com.ibm.adtech.boca.common.exceptions.ReplicationException;
(14)  import com.ibm.adtech.boca.rdf.ACLUtil;
(15)  import com.ibm.adtech.boca.rdf.repository.BOCAFactory;
(16)  import com.ibm.adtech.boca.rdf.repository.User;
(17)  import com.ibm.adtech.boca.services.IReplicationService;
(18)  import com.ibm.adtech.boca.services.ModelServiceProperties;
(19)  
(20)  public class AccessControl {
(21)  
(22)      public static void main(String[] args) throws Exception {
(23)  
(24)          final String sysadmin = "http://boca.adtech.internet.ibm.com/users/sysadmin";
(25)          final String sysadminpw = "123";
(26)  
(27)          DatasetService datasetService1 = null;
(28)          DatasetService datasetService2 = null;
(29)  
(30)          try {
(31)              Properties webserviceProperties = new Properties();
(32)              webserviceProperties.load(new FileInputStream("webserviceclient.properties"));
(33)  
(34)              datasetService1 = getDatasetService(webserviceProperties, sysadmin, sysadminpw, IReplicationService.REPLICATION_MANUAL);
(35)  
(36)  
(37)              final String namedGraphURI = "http://example.org/ng1";
(38)              final NamedGraphModel newModel = datasetService1.getRemoteModel(namedGraphURI, true);
(39)  
(40)              Resource res1 = ResourceFactory.createResource("http://example.org/res1");
(41)              Property prop1 = ResourceFactory.createProperty("http://example.org/prop1");
(42)              Literal obj1 = newModel.createTypedLiteral("foo");
(43)              newModel.add(res1, prop1, obj1);
(44)  
(45)              datasetService1.getDatasetReplicator().replicate(true);
(46)  
(47)  
(48)  
(49)              String newUser = "http://boca.adtech.internet.ibm.com/users/newUser";
(50)              String newUserPwd = "456";
(51)              String newUserDefaultRole = "http://test.ibm.com/Role/newRole";
(52)              Resource newUserDefaultRoleR = ResourceFactory.createResource(newUserDefaultRole);
(53)  
(54)              NamedGraphModel systemModel = datasetService1.getRemoteSystemModel();
(55)  
(56)              systemModel.begin(); // beginning of transaction
(57)              try {
(58)                  User userJastorObj = BOCAFactory.createUser(newUser, systemModel.getNamedGraph().getMetaDataGraph());
(59)                  userJastorObj.setDefaultRole(Node.create(newUserDefaultRole));
(60)                  userJastorObj.setPassword(newUserPwd);
(61)  
(62)                  systemModel.commit(); // end of transaction
(63)              } catch (Exception e) {
(64)                  systemModel.abort(); // abort the transaction
(65)              }
(66)  
(67)              datasetService1.getDatasetReplicator().replicate(true);
(68)  
(69)  
(70)              datasetService1.getTransactionQueueHandler().begin();
(71)              try {
(72)  
(73)                  ACLUtil.setPermissions(newModel.getNamedGraph(), newUserDefaultRoleR, true, true, true, true, true, true);
(74)                  datasetService1.getTransactionQueueHandler().commit();
(75)  
(76)              } catch (Exception e) {
(77)                  datasetService1.getTransactionQueueHandler().abort();
(78)                  throw e;
(79)              }
(80)              datasetService1.getDatasetReplicator().replicate(true);
(81)  
(82)              datasetService2 = getDatasetService(webserviceProperties, newUser, newUserPwd, IReplicationService.REPLICATION_MANUAL);
(83)  
(84)              final NamedGraphModel newModel2 = datasetService2.getRemoteModel(namedGraphURI, false);
(85)  
(86)  
(87)              newModel2.close();
(88)  
(89)          } finally {
(90)              try {
(91)                  if (datasetService1 != null)
(92)                      datasetService1.close();
(93)              } finally {
(94)                  if (datasetService2 != null)
(95)                      datasetService2.close();
(96)              }
(97)  
(98)          }
(99)      }
(100) 
(101)     public static DatasetService getDatasetService(Properties props, String username, String password, int replicationMode) throws ReplicationException {
(102)         DatasetService datasetService;
(103)         Properties properties = new Properties();
(104)         properties.putAll(props);
(105)         ModelServiceProperties.setUserName(properties, username);
(106)         ModelServiceProperties.setPassword(properties, password);
(107)         datasetService = new DatasetService(properties);
(108)         datasetService.getDatasetReplicator().setReplicationMode(replicationMode);
(109)         return datasetService;
(110)     }
(111) 
(112) }

(24-25) show the user and password of the administrator. If you have looked at the configuration files described in BocaSetup and BocaConfiguration you will recognize them. (34) initializes the DatasetService with the administrative login so that it may be used to access the metadata graph of the defaultSystemGraph, where access control information lives. Note that the metdata graph is not actually a general purpose named graph, and only accepts triples pertaining to access control. (37-45) populate a regular named graph with a statement so we have something to talk permissions about. (49-52) define a new user and its role. (54) loads a named graph model for the system graph. (56) begins a transaction. Note that it is very important that creation of a user and setting of the default role occur in the same transaction. (58) uses the Jastor generator API to create a user object in the metadata graph of the default system graph. (59-60) specify the role and password, and we commit the transaction on (62). (67) replicates the added user to the server. (73) uses a utility function to update the ACL of the newly greated named graph. It adds the role of the new user to the ACL with all permissions over the named graph. This too must be replicated to the server (80). (82) creates a new DatasetService using the new user, and accesses the named graph for which he now has permissions.

Example 12: Resetting the database programatically

In course of development and testing, you will probably want to clear your database several times. This can be easily done by dropping tables or deleting the database itself. However, it's a bit easier it use the built in reset operation on the model service. Before proceeding we warn that the reset call may be fairly expensive if the database is relatively full, and problems can arise if reset is called many times in rapid succession.

Reset takes as an argument, an RDF document representing the initial set of named graphs, users and access controls. This RDF document may not contain named graph data itself. The file is isomorphic to the initialize.nt file that is used to initialize a new Boca database the first time it starts up. For more information on the contents of these RDF documents, checkout BocaSecurity and BocaConfiguration.


(1)   package com.ibm.adtech.boca.sample;
(2)   
(3)   import java.io.FileInputStream;
(4)   import java.util.Properties;
(5)   
(6)   import com.hp.hpl.jena.rdf.model.Model;
(7)   import com.hp.hpl.jena.rdf.model.ModelFactory;
(8)   import com.ibm.adtech.boca.client.DatasetService;
(9)   
(10)  public class ResetDatabaseEmbedded {
(11)  
(12)      public static void main(String[] args) throws Exception {
(13)  
(14)          DatasetService datasetService = null;
(15)          try {
(16)              Properties embeddedProperties = new Properties();
(17)              embeddedProperties.load(new FileInputStream("embeddedclient.properties"));
(18)              datasetService = new DatasetService(embeddedProperties);
(19)              FileInputStream initialRDF = new FileInputStream("empty.rdf");
(20)              Model model = ModelFactory.createDefaultModel();
(21)              model.read(initialRDF, "");
(22)              datasetService.getModelService().reset(model.getGraph());
(23)          } finally {
(24)              if (datasetService != null) {
(25)                  datasetService.close();
(26)              }
(27)          }
(28)  
(29)      }
(30)  
(31)  }

In this example we happen to choose embedded mode (17), but it works just the same in Web Service mode. (19-21) reads the RDF document from the file system into a Jena in-memory model. (22) shows how to invoke reset on the model service.

Personal tools