JBoss.orgCommunity Documentation

Chapter 8. Configuration

8.1. Configuring ModeShape
8.1.1. Loading from a Configuration File
8.1.2. Programmatic Configuration
8.1.3. Loading from a Configuration Repository
8.2. Deploying ModeShape via JNDI
8.2.1. Example application using JCR and JNDI
8.2.2. Configuring JCR and JNDI
8.3. Setting the Classpath
8.3.1. Building against ModeShape via Maven
8.3.2. Add dependencies for logging
8.3.3. Building against ModeShape via JARs
8.4. What's next

Using ModeShape within your application is actually quite straightforward, and with JCR 2.0 it is possible for your application to do everything using only the JCR 2.0 API. Your application will first obtain a javax.jcr.Repository instance, and will use that object to create sessions through which your application will read, modify, search, or monitor content in the repository.

However, before you can use ModeShape, you need to configure it, and that's what this chapter covers.

There really are three options:

Each of these approaches has their obvious advantages, so the choice of which one to use is entirely up to you.

Loading the ModeShape configuration from a file is actually very simple:

JcrConfiguration config = new JcrConfiguration();

configuration.loadFrom(file);

where the file parameter can actually be a File instance, a URL to the file, an InputStream containing the contents of the file, or even a String containing the contents of the file.

Note

The loadFrom(...) method can be called any number of times, but each time it is called it completely wipes out any current notion of the configuration and replaces it with the configuration found in the file.

There is an optional second parameter that defines the Path within the configuration file identifying the parent node of the various configuration nodes. If not specified, it assumes "/". This makes it possible for the configuration content to be located at a different location in the hierarchical structure. (This is not often required, but when it is required this second parameter is very useful.)

Here is the configuration file that is used in the repository example, though it has been simplified a bit and most comments have been removed for clarity):


<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns:mode="http://www.modeshape.org/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0">
  <!-- 
  Define the JCR repositories 
  -->
  <mode:repositories>
      <!-- 
      Define a JCR repository that accesses the 'Cars' source directly.
      This of course is optional, since we could access the same content through 'vehicles'.
      -->
      <mode:repository jcr:name="car repository" mode:source="Cars">
          <mode:options jcr:primaryType="mode:options">
              <jaasLoginConfigName jcr:primaryType="mode:option" mode:value="modeshape-jcr"/>
          </mode:options>
          <mode:descriptors>
            <!-- 
                This adds a JCR Repository descriptor named "myDescriptor" with a value of "foo".
                So this code:
                Repository repo = ...;
                System.out.println(repo.getDescriptor("myDescriptor");

                Will now print out "foo".
            -->
            <myDescriptor mode:value="foo" />
          </mode:descriptors>
          <!-- 
                Import the custom node types defined in the named resource (a file at a 
                classpath-relative path).  If there was more than one file with custom node 
                types, we could either add successive <jcr:nodeTypes ... /> elements or just 
                add all of the files as a comma-delimited string in the mode:resource property.
            -->
          <jcr:nodeTypes mode:resource="/tck/tck_test_types.cnd" />
      </mode:repository>
  </mode:repositories>
   <!-- 
   Define the sources for the content. These sources are directly accessible using the 
   ModeShape-specific Graph API.
   -->
   <mode:sources jcr:primaryType="nt:unstructured">
       <mode:source jcr:name="Cars" 
              mode:classname="org.modeshape.graph.connector.inmemory.InMemoryRepositorySource" 
              mode:retryLimit="3" mode:defaultWorkspaceName="workspace1">
               <mode:predefinedWorkspaceNames>workspace2</mode:predefinedWorkspaceNames>
               <mode:predefinedWorkspaceNames>workspace3</mode:predefinedWorkspaceNames>
       </mode:source>
   </mode:sources>
   <!-- 
   Define the sequencers. This is an optional section. For this example, we're not using any sequencers. 
   -->
   <mode:sequencers>
       <!--mode:sequencer jcr:name="Image Sequencer">
           <mode:classname>
            org.modeshape.sequencer.image.ImageMetadataSequencer
           </mode:classname>
           <mode:description>Image metadata sequencer</mode:description>        
           <mode:pathExpression>/foo/source => /foo/target</mode:pathExpression>
           <mode:pathExpression>/bar/source => /bar/target</mode:pathExpression>
       </mode:sequencer-->
   </mode:sequencers>
   <mode:mimeTypeDetectors>
       <mode:mimeTypeDetector jcr:name="Detector" 
                             mode:description="Standard extension-based MIME type detector"/>
   </mode:mimeTypeDetectors>
</configuration>

Defining the configuration programmatically is not terribly complicated, and it for obvious reasons results in more verbose Java code. But this approach is very useful and often the easiest approach when the configuration must change or is a reflection of other dynamic information.

The JcrConfiguration class was designed to have an easy-to-use API that makes it easy to configure each of the different kinds of components, especially when using an IDE with code completion. Here are several examples:

Each repository source definition must include the name of the RepositorySource class as well as each bean property that should be set on the object:

JcrConfiguration config = ...

config.repositorySource("source A")
     .usingClass(InMemoryRepositorySource.class)
     .setDescription("The repository for our content")
     .setProperty("defaultWorkspaceName", workspaceName);

This example defines an in-memory source with the name "source A", a description, and a single "defaultWorkspaceName" bean property. Different RepositorySource implementations will the bean properties that are required and optional. Of course, the class can be specified as Class reference or a string (followed by whether the class should be loaded from the classpath or from a specific classpath).

Note

Each time repositorySource(String) is called, it will either load the existing definition with the supplied name or will create a new definition if one does not already exist. To remove a definition, simply call remove() on the result of repositorySource(String). The set of existing definitions can be accessed with the repositorySources() method.

Each defined sequencer must specify the name of the StreamSequencer implementation class as well as the path expressions defining which nodes should be sequenced and the output paths defining where the sequencer output should be placed (often as a function of the input path expression).

JcrConfiguration config = ...

config.sequencer("Image Sequencer")
     .usingClass("org.modeshape.sequencer.image.ImageMetadataSequencer")
     .loadedFromClasspath()
     .setDescription("Sequences image files to extract the characteristics of the image")
     .sequencingFrom("//(*.(jpg|jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd)[*])/jcr:content[@jcr:data]")
     .andOutputtingTo("/images/$1");

This shows an example of a sequencer definition named "Image Sequencer" that uses the ImageMetadataSequencer class (loaded from the classpath), that is to sequence the "jcr:data" property on any new or changed nodes that are named "jcr:content" below a parent node with a name ending in ".jpg", ".jpeg", ".gif", ".bmp", ".pcx", ".iff", ".ras", ".pbm", ".pgm", ".ppm" or ".psd". The output of the sequencing operation should be placed at the "/images/$1" node, where the "$1" value is captured as the name of the parent node. (The capture groups work the same way as regular expressions.) Of course, the class can be specified as Class reference or a string (followed by whether the class should be loaded from the classpath or from a specific classpath).

Note

Each time sequencer(String) is called, it will either load the existing definition with the supplied name or will create a new definition if one does not already exist. To remove a definition, simply call remove() on the result of sequencer(String). The set of existing definitions can be accessed with the sequencers() method.

Note that in addition to including a description for the configuration, it is also possible to set sequencer-specific properties using the setProperty(String,String[]) method. When ModeShape uses this configuration to set up a sequencing operation, it will instantiate the StreamSequencer class and will call a JavaBean-style setter method for each property. For example, calling setProperty("foo","val1") on the sequencer configuration will mean that ModeShape will instantiate the sequencer implementation and will look for a setFoo(String) method on the sequencer implementation class, and use that method (if found) to pass the "val1" value to the instance.

Each defined MIME type detector must specify the name of the MimeTypeDetector implementation class as well as any other bean properties required by the implementation.

JcrConfiguration config = ...

config.mimeTypeDetector("Extension Detector")
     .usingClass(org.modeshape.graph.mimetype.ExtensionBasedMimeTypeDetector.class);

Of course, the class can be specified as Class reference or a string (followed by whether the class should be loaded from the classpath or from a specific classpath).

Note

Each time mimeTypeDetector(String) is called, it will either load the existing definition with the supplied name or will create a new definition if one does not already exist. To remove a definition, simply call remove() on the result of mimeTypeDetector(String). The set of existing definitions can be accessed with the mimeTypeDetectors() method.

Regardless of how the JcrConfiguration is loaded, it can also be stored to a file or stream in an XML format that can then be reloaded in the future to recreate the configuration. This makes it very easy to programmatically generate a configuration file once while being able to load that same configuration at a later time (or on a different instance).

JcrConfiguration config = ...

String pathToFile = ...
// Save any changes before this point in the configuration repository ...
configuration.save();
// And now write out the configuration repository to a file ...
configuration.storeTo(pathToFile);

This will create a file at pathToFile that contains the current configuration in XML format. Any changes made after the most recent call to the save() method on the JcrConfiguration object will not be saved in the configuration repository, and thus will not be in the generated file. The generated XML will not be formatted to maximize human readability.

Loading the ModeShape configuration from an existing repository is also pretty straightforward. Simply create and configure the RepositorySource instance to point to the desired repository, and then call the loadFrom(RepositorySource source) method:

RepositorySource configSource = ...

JcrConfiguration config = new JcrConfiguration();
configuration.loadFrom(configSource);

This really is a more advanced way to define your configuration, so we won't go into how you configure a RepositorySource.

Note

The loadFrom(...) method can be called any number of times, but each time it is called it completely wipes out any current notion of the configuration and replaces it with the configuration found in the file.

There is an optional second parameter that defines the name of the workspace in the supplied source where the configuration content can be found. It is not needed if the workspace is the source's default workspace. There is an optional third parameter that defines the Path within the configuration repository identifying the parent node of the various configuration nodes. If not specified, it assumes "/". This makes it possible for the configuration content to be located at a different location in the hierarchical structure. (This is not often required, but when it is required this second parameter is very useful.)

Sometimes your applications can simply define a JcrConfiguration and instantiate the JcrEngine instance directly. This is very straightforward, and this is what the examples in the Getting Started guide do.

Web applications are a different story. Often, you may not want your web application to contain the code that initializes a ModeShape engine. Or, you may want the same JcrEngine instance to be reused in multiple web applications deployed to the same web/application server. In these cases, it is possible to configure the web/app server's JNDI instance to instantiate the JcrEngine, meaning the web applications need only use the standard JNDI and JCR APIs.

Here's an example of how such a web application would obtain a JCR Repository instance, use it to create a JcrSession, and then close the session when completed.

Session session = null;

try {
  // Look up the JCR Repository object ...
    InitialContext initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    // name in JNDI is defined by configuration
    Repository repo = (Repository) envCtx.lookup("jcr/local");  
    // Obtain a JCR Session using simple authentication
    // (or use anonymous authentication if desired)
    session = repo.login(new SimpleCredentials("username", "password".toCharArray()));
    // Use the JCR Session to do something interesting
} catch (Exception ex) {
    ex.printStackTrace();
} finally {
    if (session != null) session.logout();
}

Note that the location of the Repository instance in JNDI depends upon the configuration. In this example, we used "jcr/local", but the only requirement is that it match the location where it was placed in JNDI.

We showed how web applications can use an existing Repository instance. In the next section, we describe how to configure the web server so that the Repository instance is available in JNDI.

Each kind of web server or application server is different, but all servlet containers do provide a way of configuring objects and placing them into JNDI. ModeShape provides a JndiRepositoryFactory class that implements and that can be used in the server's configuration. The JndiRepositoryFactory requires two properties:

  • configFile is the path to the configuration file. ModeShape will first treat the value of this property as a resource name and attempt to load it from the classpath. If no resource can be found with that name, ModeShape will assume that it is supposed to be a filename (with or without a path) and load it from the filesystem.

  • repositoryName is the name of a JCR repository that exists in the JcrConfiguration and that will be made available by this JNDI entry

Here's an example of a fragment of the conf/context.xml for Tomcat:


<Resource name="jcr/local" 
          auth="Container"
          type="javax.jcr.Repository"
          factory="org.modeshape.jcr.JndiRepositoryFactory"
          configFile="/resource/path/to/configuration.xml"
          repositoryName="Test Repository Source" />

Note that it is possible to have multiple Resource entries. The JndiRepositoryFactory ensures that only one JcrEngine is instantiated, but that a Repository instance is registered for each entry.

Before the server can start, however, all of the ModeShape jars need to be placed on the classpath for the server. JAAS also needs to be configured, and this can be done using the application server's configuration or in your web application if you're using a simple servlet container.

Note

The ModeShape community has solicited input on how we can make it easier to consume and use ModeShape in applications that do not use Maven. Check out the discussion thread, and please add any suggestions or opinions!

Then, your web application needs to reference the Resource and state its requirements in its web.xml:


<resource-env-ref>
   <description>Repository</description>
   <resource-env-ref-name>jcr/local</resource-env-ref-name>
   <resource-env-ref-type>javax.jcr.Repository</resource-env-ref-type>
</resource-env-ref>

Note that the value of resource-env-ref-name matches the value of the name attribute on the <Resource> tag in the context.xml described above. This is a must.

At this point, your web application can perform the lookup of the Repository object, create and use a Session, and then close the Session. Here's an example of a JSP page that does this:

<%@ page import="

    javax.naming.*,
    javax.jcr.*,
    org.jboss.security.config.IDTrustConfiguration
    " %>
<%!
static {
    // Initialize IDTrust
    String configFile = "security/jaas.conf.xml";
    IDTrustConfiguration idtrustConfig = new IDTrustConfiguration();
    try {
        idtrustConfig.config(configFile);
    } catch (Exception ex) {
        throw new IllegalStateException(ex);
    }
}
%>
<%
Session sess = null;
try {
    InitialContext initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    Repository repo = (Repository) envCtx.lookup("jcr/local");
    sess = repo.login(new SimpleCredentials("readwrite", "readwrite".toCharArray()));
    // Do something interesting with the Session ...
    out.println(sess.getRootNode().getPrimaryNodeType().getName());
} catch (Exception ex) {
    ex.printStackTrace();
} finally {
    if (sess != null) sess.logout();
}
%>

Since this uses a servlet container, there is no JAAS implementation configured, so note the loading of IDTrust to create the JAAS realm. (To make this work in Tomcat, the security folder that contains the jaas.conf.xml, users.properties, and roles.properties needs to be moved into the %CATALINA_HOME% directory. Moving the security folder into the conf directory does not allow those files to be visible to the JSP page.)

Note

If you use an application server such as JBoss EAP, you could just configure the JAAS realm as part of the server configuration and be done with it.

Before you deploy ModeShape into your application or its environment, you need to make sure that all of the ModeShape JARs are on the appropriate classpath. Two different scenarios are covered in this section: Maven-based, and using JARs with the traditional classpath.

By far the easiest way to use ModeShape is to use Maven, because with just a few lines of code, Maven will automatically pull all the JARs and source for all of the ModeShape libraries as well as everything those libraries need. All of ModeShape's artifacts for each release are published in the new JBoss Maven repository under the "org.modeshape" group ID.

The JBoss Maven repository not only contains all of the artifacts for ModeShape and other open source projects hosted at JBoss.org, but it also proxies quite a few other repositories that contain many other third-party libraries.

So if you're using Maven (or Ivy), first make sure your project knows about this new JBoss Maven repository. One way to do this is to add the following to your project POM (you'll still likely want to use other Maven repositories for third-party artifacts):


<repositories>
  <repository>
    <id>jboss</id>
    <url>http://repository.jboss.org/nexus/content/groups/public/</url>
  </repository>
</repositories>

Or, you can add this information to your ~/.m2/settings.xml file. For more information, see the JBoss wiki page.

Then, simply modify your project's POM by adding dependencies on the ModeShape JCR library:


<dependency>
  <groupId>org.modeshape</groupId>
  <artifactId>modeshape-jcr</artifactId>
  <version>2.0.0.Final</version>
</dependency>

This adds only the minimal libraries required to use ModeShape, so you need to add dependencies for each of the connectors and sequencers you want to use. Here is the list of available sequencers:


<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-cnd</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-ddl</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshapce</groupid>
  <artifactid>modeshape-sequencer-images</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-classfile</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-java</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-mp3</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-msoffice</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-xml</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-text</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-zip</artifactid>
  <version>2.0.0.Final</version>
</dependency>

Here is the list of available connectors:


<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-filesystem</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-infinispan</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-jcr</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-jbosscache</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-jdbc-metadata</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-store-jpa</artifactid>
  <version>2.0.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-svn</artifactid>
  <version>2.0.0.Final</version>
</dependency>

The sequencer and connector libraries you choose, plus every third-party library they need, will be pulled in automatically by Maven into your project.

ModeShape is designed to use the same logging framework as your application, and it uses SLF4J to accomplish this. In other words, ModeShape depends upon the SLF4J API library, but requires you to provide provide a logging implementation as well as the appropriate SLF4J binding JAR.

For example, if your application is using Log4J, your application will already have a dependency for it, and so ModeShape log messages will be sent to the same logging system used in your application, you need to add a dependency to the SLF4J-to-Log4J binding JAR:


<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.5.11</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.16</version>
</dependency>

Of course, SLF4J works with other logging frameworks, too. Some logging implementations (such as LogBack) implement the SLF4J API natively, meaning they require no binding JAR. For details on the options and how to configure them, see the SLF4J manual.

If your application doesn't use Maven, you'll need to obtain the ModeShape JARs and place them onto your application's classpath. ModeShape provides a single download with all of the JARs for all ModeShape components and all dependencies. This file contains the following:

  • modeshape-jcr-2.0.0.Final-with-dependencies.jar contains the JARs necessary to run the core ModeShape JCR repository engine, the in-memory connector, and the federating connector;

  • one modeshape-connector-<type>-2.0.0.Final-with-dependencies.jar for each type of connector, each containing the JARs necessary for that connector;

  • one modeshape-sequencer-<type>-2.0.0.Final-with-dependencies.jar for each type of sequencer, each containing the JARs necessary for that sequencer;

  • modeshape-mimetype-detector-aperture-2.0.0.Final-with-dependencies.jar contains all of the JARs required for the component that detects the MIME type of files based upon names and/or content; and

  • modeshape-jpa-ddl-gen-2.0.0.Final-jar-with-dependencies.jar contains all of the JARs required to run the DDL generation utility.

Note that the core engine is required in all configurations. The jcr-2.0.jar file is not included and must be provided by you. And, as mentioned in the previous section, ModeShape uses SLF4J for logging and you must provide a logging implementation as well as the appropriate SLF4J binding JAR.

This chapter outlines how you configure ModeShape, how to deploy ModeShape into your application, and how to set up your application's environment with the required ModeShape JARs. The next chapter talks about how your application can use the JCR API to access ModeShape repositories.