JBoss.org Community Documentation

4.1. Configuring the Sequencing Service

The JBoss DNA sequencing service is the component that manages the sequencers and that reacts to changes in JCR repositories and then running the appropriate sequencers. This involves processing the changes on a node, determinine which (if any) sequencer should be run on that node, and for each sequencer constructing the execution environment, calling the sequencer, and saving the information generated by the sequencer.

To set up the sequencing service, an instance is created and dependent components are injected into the object. This includes among other things:

  • An execution context that defines the context in which the service runs, including a factory for JCR sessions given names of the repository and workspace. This factory must be configured, and is how JBoss DNA knows about your JCR repositories and how to connect to them. More on this a bit later.

  • An optional factory for class loaders used to load sequencers. If no factory is supplied, the service uses the current thread's context class loader (or if that is null the class loader that loaded the sequencing service class).

  • An java.util.concurrent.ExecutorService used to execute the sequencing activites. If none is supplied, a new single-threaded executor is created by calling Executors.newSingleThreadExecutor(). (This can easily be changed by subclassing and overriding the SequencerService.createDefaultExecutorService() method.)

  • Filters for sequencers and events. By default, all sequencers are considered for "node added", "property added" and "property changed" events.

As mentioned above, the ExecutionContext provides access to a SessionFactory that is used by JBoss DNA to establish sessions to your JCR repositories. Two implementations are available:

  • The JndiSessionFactory looks up JCR Repository instances in JNDI using names that are supplied when creating sessions. This implementation also has methods to set the JCR Credentials for a given workspace name.

  • The SimpleSessionFactory has methods to register the JCR Repository instances with names, as well as methods to set the JCR Credentials for a given workspace name.

You can use the SimpleExecutionContext implementation of ExecutionContext and supply a SessionFactory instance, or you can provide your own implementation.

Here's an example of how to instantiate and configure the SequencingService:

SimpleSessionFactory sessionFactory = new SimpleSessionFactory();
sessionFactory.registerRepository("Main Repository", this.repository);
Credentials credentials = new SimpleCredentials("jsmith", "secret".toCharArray());
sessionFactory.registerCredentials("Main Repository/Workspace1", credentials);
ExecutionContext executionContext = new SimpleExecutionContext(sessionFactory);

// Create the sequencing service, passing in the execution context ...
SequencingService sequencingService = new SequencingService();
sequencingService.setExecutionContext(executionContext);

After the sequencing service is created and configured, it must be started. The SequencingService has an administration object (that is an instance of ServiceAdministrator) with start(), pause(), and shutdown() methods. The latter method will close the queue for sequencing, but will allow sequencing operations already running to complete normally. To wait until all sequencing operations have completed, simply call the awaitTermination method and pass it the maximum amount of time you want to wait.

sequencingService.getAdministrator().start();

The sequencing service must also be configured with the sequencers that it will use. This is done using the addSequencer(SequencerConfig) method and passing a SequencerConfig instance that can create. Here's an example:

String name = "Image Sequencer";
String desc = "Sequences image files to extract the characteristics of the image";
String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
String[] classpath = null; // Use the current classpath
String[] pathExpressions = {"//(*.(jpg|jpeg|gif|bmp|pcx|png))[*]/jcr:content[@jcr:data] => /images/$1"};
SequencerConfig imageSequencerConfig = new SequencerConfig(name, desc, classname, classpath, pathExpressions);
sequencingService.addSequencer(imageSequencerConfig);

name = "Mp3 Sequencer";
desc = "Sequences mp3 files to extract the id3 tags of the audio file";
classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
String[] mp3PathExpressions = {"//(*.mp3)[*]/jcr:content[@jcr:data] => /mp3s/$1"};
SequencerConfig mp3SequencerConfig = new SequencerConfig(name, desc, classname, classpath, mp3PathExpressions);
sequencingService.addSequencer(mp3SequencerConfig);

This is pretty self-explanatory, except for the classpath and pathExpression parameters. The classpath parameter defines the classpath that is passed to the class loader factory mentioned above. Our sequencer is on the classpath, so we can simply use null here.

The path expression is more complicated. Sequencer path expressions are used by the sequencing service to determine whether a particular changed node should be sequenced. The expressions consist of two parts: a selection criteria and an output expression. Here's a simple example:

/a/b/c@title => /d/e/f

Here, the /a/b/c@title is the selection criteria that applies when the /a/b/c node has a title property that is added or changed. When the selection criteria matches a change event, the sequencer will be run and any generated output will be inserted into the repository described by the output expression. In this example, the generated output would be placed at the /d/e/f node.

Note

Sequencer path expressions can be fairly complex and may use wildcards, specificy same-name sibling indexes, provide optional and choice elements, and may capture parts of the selection criteria for use in the output expression. The path expression used in the image sequencer configuration example above shows a more complex example:

//(*.(jpg|jpeg|gif|bmp|pcx|png))[*]/jcr:content[@jcr:data] => /images/$1

This uses "//" to select any node at any level in the repository whose name ends with "." and one of the extensions (e.g., ".jpg", ".jpeg", etc.) and that has a child node named "jcr:content" that has a "jcr:data" property. It also selects the file name as the first capture group (the first set of parentheses) for use in the output expression. In this example, any sequencer output is placed on a node with that same file name under the "/images" node.

Other things are possible, too. For example, the name of the repository/workspace (as used by the SessionFactory) may be specified at the beginning of the select criteria and/or the output expression. This means it's possible to place the sequencer output in a different repository than the node being sequenced.

For more detail about sequencer path expressions, see the org.jboss.dna.repository.sequencer.SequencerPathExpression class and the corresponding org.jboss.dna.repository.sequencer.SequencerPathExpressionTest test case.

After the service is started, it is ready to start reacting to changes in the repository. But it first must be wired to the repositories using listener. This is accomplished using the ObservationService described in the next section.