As is also explained in the official Sesame user manual, the Repository API (see also the Sesame Javadoc) is the starting point for anyone wanting to program against Sesame. While the user manual gives some good examples on getting you started with using that API, however, there are some tips and tricks for more effective use that I thought I might share, in a series of weblog postings I’m planning on “Cooking with Sesame”. This first episode, I’ll go a bit deeper into creation and management of Sesame repositories, specifically using the RepositoryManager and related classes.
Creating repositories: the basics
Sesame supports a number of different types of Repository. The two that are most commonly used are the SailRepository, for local repositories, and the HTTPRepository, a proxy for repositories on a remote Sesame server.
SailRepository is an object that represents a local Sesame database. Note that, although we call it a database, this does not automatically mean persistence, or anything “heavy” – it can be a simple in-memory representation of an RDF graph. In fact, the easiest way to load any RDF file into memory using Sesame is by creating a SailRepository using an in-memory database.
The type of database (main memory, native, relational, …) is determined by the SAIL stack provided to the SailRepository (the SAIL API is a Sesame system-internal API that is used to wrap the details of the underlying storage and reasoning mechanism). For example, for creating a simple in-memory repository, we do the following:
import org.openrdf.repository.Repository; import org.openrdf.repository.sail.SailRepository; import org.openrdf.sail.memory.MemoryStore; Repository repository = new SailRepository(new MemoryStore()); repository.initialize();
MemoryStore object referred to here is a SAIL backend that simply stores all RDF in-memory. Sesame provides various different SAIL backends, as well as various options for configuring each SAIL backend. Some further examples of the creation of various types of repositories (including also the use of the HTTPRepository) are shown in chapter 8 of the Sesame user manual.
Once you have created a Repository object like this, you can open a RepositoryConnection on it to make further use of the repository: adding or removing data, executing queries, and so on.
So far so good: we can easily create and use various different types of Sesame repositories. However, when developing an application in which you have to keep track of several repositories, sharing references to these repositories between different parts of your code can quickly become complex. Of course, you could declare some static references for use throughout your application, but if several repositories have to be juggled this way, this too can become cumbersome. Ideal would be one central location where all information on the repositories in use (including id, type, directory for persistent data storage, etc.) is kept. This is the role of the Sesame RepositoryManager.
Using the RepositoryManager for handling repository creation and administration offers a number of advantages, including:
- a single RepositoryManager object can be more easily shared throughout your application than a host of static references to individual repositories;
- you can more easily create and manage repositories ‘on-the-fly’, for example if your application requires creation of new repositories on user input;
- the RepositoryManager stores your configuration, including all repository data, in one central spot on the file system.
The RepositoryManager comes in two flavours: the LocalRepositoryManager and the RemoteRepositoryManager.
A LocalRepositoryManager manages repository handling for you locally, and is always created using a (local) directory. This directory is where all Sesame repositories handled by the manager store their data, and also where the LocalRepositoryManager itself stores its configuration data.
You create a new LocalRepositoryManager as follows:
import java.io.File; import org.openrdf.repository.manager.LocalRepositoryManager; File baseDir = new File("/path/to/storage/dir/"); LocalRepositoryManager manager = new LocalRepositoryManager(baseDir); manager.initialize();
To use a LocalRepositoryManager to create and manager repositories works slightly differently from what we’ve seen above about creating Sesame repositories. The LocalRepositoryManager works by providing it with RepositoryConfig objects, which are declarative specifications of the repository you want. You add a RepositoryConfig object for your new repository, and then request the actual Repository back from the LocalRepositoryManager:
import org.openrdf.repository.config.RepositoryConfig; String repositoryId = "test-db"; RepositoryConfig repConfig = new RepositoryConfig(repositoryId, repositoryTypeSpec); manager.addRepositoryConfig(repConfig); Repository repository = manager.getRepository(repositoryId);
In the above bit of code, you may have noticed that I provide an innocuous-looking variable called
repositoryTypeSpec to the constructor of our RepositoryConfig. This variable is an instance of a class called
RepositoryImplConfig, and this specifies the actual configuration of our new repository: what backends to use, whether or not to use inferencing, and so on.
RepositoryImplConfig object can be done in two ways: programmatically, or by reading a (RDF) config file. Here, I will show the programmatic way (in a future article we may look at the other method, using RDF config files).
import org.openrdf.sail.config.SailImplConfig; import org.openrdf.sail.memory.config.MemoryStoreConfig; import org.openrdf.repository.config.RepositoryImplConfig; import org.openrdf.repository.sail.config.SailRepositoryConfig; // create a configuration for the SAIL stack SailImplConfig backendConfig = new MemoryStoreConfig(); // create a configuration for the repository implementation RepositoryImplConfig repositoryTypeSpec = new SailRepositoryConfig(backendConfig);
As you can see, we use a class called MemoryStoreConfig for specifying the type of storage backend we want. This class resides in a
configsub-package of the memory store package (
org.openrdf.sail.memory). Each particular type of SAIL in Sesame has such a config class.
As a second example, we create a slightly more complex type of store: still in-memory, but this time we want it to use the memory store’s persistence option, and we also want to add RDFS inferencing. In Sesame, RDFS inferencing is provided by a separate SAIL implementation, which can be ‘stacked’ on top of another SAIL. We follow that pattern in the creation of our config object:
import org.openrdf.sail.inferencer.fc.config.ForwardChainingRDFSInferencerConfig; // create a configuration for the SAIL stack boolean persist = true; SailImplConfig backendConfig = new MemoryStoreConfig(persist); // stack an inferencer config on top of our backend-config backendConfig = new ForwardChainingRDFSInferencerConfig(backendConfig); // create a configuration for the repository implementation SailRepositoryConfig repositoryTypeSpec = new SailRepositoryConfig(backendConfig);
A useful feature of Sesame is that most its APIs are transparent with respect to whether you are working locally or remote. This is the case for the Sesame repositories, but also for the RepositoryManager. In the above examples, we have used a LocalRepositoryManager, creating Sesame repositories for local use. However, it is also possible to use a RemoteRepositoryManager, using it to create and manage Sesame repositories residing on a remotely running Sesame server.
A RemoteRepositoryManager is initialized as follows:
import org.openrdf.repository.manager.RemoteRepositoryManager; // URL of the remote Sesame server we want to access String serverUrl = "http://localhost:8080/openrdf-sesame"; RemoteRepositoryManager manager = new RemoteRepositoryManager(serverUrl); manager.initialize();
Once initialized, the RemoteRepositoryManager can be used in the same fashion as the LocalRepositoryManager: creating new repositories, requesting references to existing repositories, and so on.