JBoss.orgCommunity Documentation

Chapter 8. Configuration

8.1. Configuring ModeShape
8.1.1. Configuration Files
8.1.2. Programmatic Configuration
8.1.3. Loading from a Configuration Repository
8.2. Repository system content
8.3. Clustering
8.3.1. Enabling Clustering in ModeShape
8.3.2. JGroups configuration
8.4. Using ModeShape in Web Applications
8.4.1. Deploying ModeShape to JBoss AS
8.4.2. Deploying ModeShape to Tomcat
8.5. Setting the Classpath
8.5.1. Building against ModeShape via Maven
8.5.2. Add dependencies for logging
8.5.3. Building against ModeShape via JARs
8.6. 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.

By far the easiest approach to defining your ModeShape configuration is to use a configuration file. As mentioned above, you'll want to do this if your application uses the standard and implementation-independent RepositoryFactory mechanism to obtain the JCR Repository reference.

Here is an example configuration file used in the repository example covered in the Getting Started document, though it has been slightly simplified 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 list the files as a single comma-delimited string
                in the mode:resource property, or list them individually using multiple 
                mode:resource child elements.
            -->
          <jcr:nodeTypes>
               <mode:resource>/org/example/my-node-types.cnd</mode:resource>
               <mode:resource>/org/example/additional-node-types.cnd</mode:resource>
            </jcr:nodeTypes>
      </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>

Most likely you'll define your configuration in a file. But there are some situations where it's far easier - even necessary - to programmatically configure ModeShape. For example, you may not be able to predefine a configuration, because it needs parameters and information known only at runtime.

One obvious approach is to write code that takes this new information and generates a ModeShape configuration file. The challenge here is that a sizable amount of code may be required just to write out the XML file in the correct format.

Perhaps an easier approach is to use the ModeShape JcrConfiguration class to programmatically construct the configuration, and then have it write the configuration out to a file. You can even load a starting configuration, programmatically modify it, and write it out to a file. From there, your application can use the standard and implementation-independent JCR API to find and use the Repository instances.

The JcrConfiguration class is used by ModeShape to read in the configuration files, but it was also 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. The next few sections describe how to configure the various parts of a ModeShape configuration.

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, so it may be a bit hard to read. (Any good XML editor will be able to format it for readability.)

So far, we've seen how to load a configuration from a file, how to programmatically define a configuration and write it out to a file. In this section, we'll see how ModeShape can load its configuration from another repository.

The first step is to create and configure the RepositorySource instance that we'll use to access the repository where the configuration is stored. Then, create a JcrConfiguration instance and load from this source:

RepositorySource configSource = ...

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

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 it is very useful if you ModeShape configuration file is embedded within another XML file.)

Once the JcrConfiguration has been loaded from a RepositorySource, the JcrConfiguration instance can be used to modify the configuration and then save those changes back to the repository. This technique can be used to place a configuration into a repository (such as a database) for the first time:

RepositorySource configSource = ... // a RepositorySource to an empty source

JcrConfiguration config = new JcrConfiguration();
// Bind the configuration to the repository source (which is initially empty)...
configuration.loadFrom(configSource);
// Now load a configuration from a file (or construct one programmatically) ...
String pathToFile = ... 
configuration.loadFrom(pathToFile);
// Now save the configuration into the source ...
configuration.save();

Now you can load this configuration in multiple processes, using the approach mentioned above.

Each JCR repository contains information about the system in the "/jcr:system" area of the repository content. All of this system content applies to the whole repository (e.g., namespaces, node types, locks, versions, etc.) and therefore every session for each workspace sees the exact same "/jcr:system" content.

ModeShape implements this behavior by storing all "/jcr:system" content in a separate workspace, and then using federation to project that content into each workspace. This ensures that all workspaces see the same content, without having to duplicate the "/jcr:system" content in each workspace and ensure those copies stay in sync. Federation is better than duplication.

By default, ModeShape creates this separate system workspace in a transient, in-memory store. This works great for some simplistic cases, but this doesn't work when using clustering, versioning, or dynamically registering namespaces or adding or changing node types. This is because these features all rely upon changing or adding content in the "/jcr:system" area. For example, version histories are stored under "/jcr:system/jcr:versionStorage", node types under "/jcr:system/jcr:versionStorage", and namespaces under "/jcr:system/mode:namespaces".

In these situations, it is necessary to persist the system content in a repository source, and if clustering is enabled this source needs to be accessible to all members of the cluster. Many times, the easiest approach is to simply define an extra workspace in your repository source where the system content can be stored. It's also possible to define a separate repository source with a separate workspace for each repository's system content. (Using a separate source is required when the repository is using a single repository source that can only store limited kinds of nodes, like the file system connector or Subversion connector that can only store nt:file and nt:folder nodes.)

You should always configure each ModeShape repository with a source for its system workspace by using the SYSTEM_WORKSPACE_NAME repository option with a value that defines the name of source and name of the workspace in that source where the system content should be stored, in the format:

  workspaceName@sourceName

This specifies the system content should be stored in the workspace named "workspaceName" in the "sourceName" repository source.

The system content can be stored in any repository source capable of storing any content and, in the case of clustering, is accessible across multiple processes. For most people, this will mean a relational database. Here is an abbreviated example of an XML configuration that defines a source for the system storage (in a MySQL database) and a repository that uses it:


<?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">
  <mode:repositories>
    <mode:repository jcr:name="car repository" mode:source="Cars">
      <mode:options jcr:primaryType="mode:options">
        <!-- Explicitly specify the "system" workspace in the "SystemStore" source. -->
        <systemWorkspaceName jcr:primaryType="mode:option" 
                               mode:value="system@SystemStore"/>
        ...
      </mode:options>
      ...
    </mode:repository>
    ...
  </mode:repositories>
  <mode:sources jcr:primaryType="nt:unstructured">
    <!-- One source for the "/jcr:system" content ... -->
    <mode:source jcr:name="SystemStore" 
                 mode:classname="org.modeshape.connector.store.jpa.JpaSource"
                 mode:description="The database store for our system content"
                 mode:dialect="org.hibernate.dialect.MySQLDialect"
                 mode:dataSourceJndiName="java:/MyDataSource"
                 mode:defaultWorkspaceName="system"
                 mode:autoGenerateSchema="validate"/>    
    </mode:sources>
    <!-- An another source for the regular content ... -->
    <mode:source jcr:name="Cars" 
                 mode:classname="org.modeshape.connector.store.jpa.JpaSource"
                 mode:description="The database store for our system content"
                 mode:dialect="org.hibernate.dialect.MySQLDialect"
                 mode:dataSourceJndiName="java:/MyDataSource"
                 mode:defaultWorkspaceName="workspace1"
                 mode:autoGenerateSchema="validate">
      <mode:predefinedWorkspaceNames>workspace1</mode:predefinedWorkspaceNames>
      <mode:predefinedWorkspaceNames>workspace2</mode:predefinedWorkspaceNames>
      <mode:predefinedWorkspaceNames>workspace3</mode:predefinedWorkspaceNames>
    </mode:sources>
    ...
  </mode:sources>
  ...
</configuration>

Of course, you can always use a separate workspace in your main source, too:


<?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">
  <mode:repositories>
    <mode:repository jcr:name="car repository" mode:source="Cars">
      <mode:options jcr:primaryType="mode:options">
        <!-- Explicitly specify the "system" workspace in the "Cars" source. -->
        <systemWorkspaceName jcr:primaryType="mode:option" mode:value="system@Cars"/>
        ...
      </mode:options>
      ...
    </mode:repository>
    ...
  </mode:repositories>
  <mode:sources jcr:primaryType="nt:unstructured">
    <!-- 
    Define one source for the regular content with a special workspace for the system content.
    -->
    <mode:source jcr:name="Cars" 
                 mode:classname="org.modeshape.connector.store.jpa.JpaSource"
                 mode:description="The database store for our system content"
                 mode:dialect="org.hibernate.dialect.MySQLDialect"
                 mode:dataSourceJndiName="java:/MyDataSource"
                 mode:defaultWorkspaceName="workspace1"
                 mode:autoGenerateSchema="validate">
      <mode:predefinedWorkspaceNames>workspace1</mode:predefinedWorkspaceNames>    
      <mode:predefinedWorkspaceNames>workspace2</mode:predefinedWorkspaceNames>    
      <mode:predefinedWorkspaceNames>workspace3</mode:predefinedWorkspaceNames>    
      <mode:predefinedWorkspaceNames>system</mode:predefinedWorkspaceNames>    
    </mode:sources>
    ...
  </mode:sources>
  ...
</configuration>

ModeShape 2.1 introduced the ability to have a cluster of JcrEngine instances distributed across multiple processes while behaving as though everything was happening in a single process. With clusters, the workload can be distributed across multiple machines, increasing tolerance against failure while allowing ModeShape to scale out to handle more workload.

ModeShape clustering uses the powerful, flexible and mature JGroups library to handle all network communication within the cluster. JGroups provides a wealth of capabilities, including automatically detecting new engines in the cluster (called discovery), reliable multicast communication, and automatic determination of the master node in the cluster. JGroups has a flexible protocol stack, works across firewalls, WANs and LANs, and supports multiple transport protocols, failure detection, reliable unicast and multicast message transmission, and encryption.

By default, clustering is not enabled. This means that each JcrEngine instance is self-contained and will not be aware of changes made in other JcrEngine instances. This is perfect in many lightweight or embedded scenarios, because it does not introduce any overhead associated with network communication.

However, clustering ModeShape is very easy and requires only a few simple steps:

  1. Enable clustering in the ModeShape configuration (more on this in a bit).

  2. Include the modeshape-clustering module in your application, either by JAR file or Maven dependency.

  3. Start (or deploy) multiple JcrEngine instances using the same configuration. For embedded scenarios, this means simply instantiating multiple JcrEngine instances in multiple processes. In other cases, this means deploying ModeShape to multiple servers (either using the WebDAV server, REST server, or into JNDI and using with your own applications).

Your JCR-based application doesn't need to change in any other ways. Any implementations registered in Sessions on any of the engines will be notified of all events, regardless of whether those events were due to changes in the local or remote engines.

It also doesn't matter how many Repository instances are defined in the configuration and managed by each JcrEngine instance: each engine in the cluster can manage multiple named repositories. ModeShape ensures that all Sessions for a named repository see the changes made to that repository, regardless of where those sessions are located in the cluster. Likewise, those same changes will not be visible to the sessions for any other named repository.

A ModeShape configuration can have a "clustering" fragment that defines the name of the cluster and the JGroups configuration:


<clustering clusterName="modeshape-cluster" configuration="jgroups-modeshape.xml" />

The "clusterName" is a string that is a logical name of the cluster; all engines connecting to the same name form a cluster. Any messages multicast from one engine in the cluster will be received by all other members of the cluster. Again, the cluster name is independent of the repositories managed by th

The "configuration" value is a string that is one of:

The format of this JGroups configuration will be described in the next section. If the "configuration" property is not given, ModeShape will use the default JGroups configuration (as defined by the specific JGroups version).

Note

Note that all engines in the cluster must have the same JGroups configuration. In fact, all engines in the cluster will almost always have exactly the same ModeShape configuration.

Here is an example of a "clustering" fragment defining a cluster named "modeshape-cluster" using the JGroups configuration defined in the "jgroups-modeshape.xml" file at the supplied URL:


<clustering clusterName="modeshape-cluster" 
      configuration="file://some/path/jgroups-modeshape.xml" />

This next example uses the JGroups configuration defined in the "jgroups-modeshape.xml" resource file on the classpath (or as an absolute path on a *nix system):


<clustering clusterName="modeshape-cluster" 
      configuration="/some/path/jgroups-modeshape.xml" />

Next is an example that specifies the JGroups configuration using the older string representation of the form:


<clustering clusterName="modeshape-cluster" 
      configuration="PROTOCOL(param=value;param=value):PROTOCOL:PROTOCOL" />

Of course, the "configuration" property can be specified as a child element, too (line breaks added for readability):


<clustering clusterName="modeshape-cluster">
         <configuration>UDP(max_bundle_size="60000":max_bundle_timeout="30"):
                          PING(timeout="2000"):...</configuration>
</clustering>

And finally an example that specifies the JGroups configuration using the newer XML representation (line breaks added for readability):


<clustering clusterName="modeshape-cluster">
     <configuration><![CDATA[<config><UDP max_bundle_size="60000" 
          max_bundle_timeout="30".../><PING timeout="2000"/>...</config>]]>
     </configuration>
</clustering>

Note that the this example uses a child XML element for the "configuration", along with a CDATA section, so that the XML configuration can be nested within the ModeShape configuration.

Warning

Remember to specify the system workspace name for each repository that is clustered.

The JGroups configuration defines a protocol stack that is used for messaging, starting with the bottom-most protocol and ending with the top-most protocol.

An example of the newer-style JGroups XML format is:


<config>
   <UDP
        mcast_addr="${jgroups.udp.mcast_addr:228.10.10.10}"
        mcast_port="${jgroups.udp.mcast_port:45588}"
        discard_incompatible_packets="true"
        max_bundle_size="60000"
        max_bundle_timeout="30"
        ip_ttl="${jgroups.udp.ip_ttl:2}"
        enable_bundling="true"
        thread_pool.enabled="true"
        thread_pool.min_threads="1"
        thread_pool.max_threads="25"
        thread_pool.keep_alive_time="5000"
        thread_pool.queue_enabled="false"
        thread_pool.queue_max_size="100"
        thread_pool.rejection_policy="Run"
        oob_thread_pool.enabled="true"
        oob_thread_pool.min_threads="1"
        oob_thread_pool.max_threads="8"
        oob_thread_pool.keep_alive_time="5000"
        oob_thread_pool.queue_enabled="false"
        oob_thread_pool.queue_max_size="100"
        oob_thread_pool.rejection_policy="Run"/>
   <PING timeout="2000"
           num_initial_members="3"/>
   <MERGE2 max_interval="30000"
           min_interval="10000"/>
   <FD_SOCK/>
   <FD timeout="10000" max_tries="5" />
   <VERIFY_SUSPECT timeout="1500"  />
   <BARRIER />
   <pbcast.NAKACK
                  use_mcast_xmit="false" gc_lag="0"
                  retransmit_timeout="300,600,1200,2400,4800"
                  discard_delivered_msgs="true"/>
   <UNICAST timeout="300,600,1200,2400,3600"/>
   <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
                  max_bytes="400000"/>
   <VIEW_SYNC avg_send_interval="60000"   />
   <pbcast.GMS print_local_addr="true" join_timeout="3000"
               view_bundling="true"/>
   <FC max_credits="20000000"
                   min_threshold="0.10"/>
   <FRAG2 frag_size="60000"  />
   <pbcast.STATE_TRANSFER  />
</config>

The older-style JGroups string format is of the form:

PROTOCOL(param1=value1:param2=value2):PROTOCOL:PROTOCOL

This format is generally harder to read and generally discouraged. Nevertheless, here's an example of the older string format defining the same stack as the previous XML example (line breaks have been added for readability):

UDP(
        mcast_addr="${jgroups.udp.mcast_addr:228.10.10.10}":
        mcast_port="${jgroups.udp.mcast_port:45588}":
        discard_incompatible_packets="true":
        max_bundle_size="60000":
        max_bundle_timeout="30":
        ip_ttl="${jgroups.udp.ip_ttl:2}":
        enable_bundling="true":
        thread_pool.enabled="true":
        thread_pool.min_threads="1":
        thread_pool.max_threads="25":
        thread_pool.keep_alive_time="5000":
        thread_pool.queue_enabled="false":
        thread_pool.queue_max_size="100":
        thread_pool.rejection_policy="Run":
        oob_thread_pool.enabled="true":
        oob_thread_pool.min_threads="1":
        oob_thread_pool.max_threads="8":
        oob_thread_pool.keep_alive_time="5000":
        oob_thread_pool.queue_enabled="false":
        oob_thread_pool.queue_max_size="100":
        oob_thread_pool.rejection_policy="Run"):
   PING(timeout="2000":
        num_initial_members="3"):
   MERGE2(max_interval="30000":
          min_interval="10000"):
   FD_SOCK:
   FD(timeout="10000":max_tries="5"):
   VERIFY_SUSPECT(timeout="1500"):
   BARRIER:
   pbcast.NAKACK(use_mcast_xmit="false":gc_lag="0":
                 retransmit_timeout="300,600,1200,2400,4800":
                 discard_delivered_msgs="true"):
   UNICAST(timeout="300,600,1200,2400,3600"):
   pbcast.STABLE(stability_delay="1000":desired_avg_gossip="50000":
                 max_bytes="400000"):
   VIEW_SYNC(avg_send_interval="60000"):
   pbcast.GMS(print_local_addr="true":join_timeout="3000"
              view_bundling="true"):
   FC(max_credits="20000000":
      min_threshold="0.10"):
   FRAG2(frag_size="60000"):
   pbcast.STATE_TRANSFER

For more details on how to configure the JGroups stack, see the JGroups Manual.

Note

JGroups is also used in Infinispan, JBoss AS, and other open source projects, and many of the JGroups configurations will work with ModeShape deployed in those same environments. For example, this blog post describes how to configure JGroups with three autodiscovery options available on Amazon EC2.

Sometimes your applications can simply define a configuration file and use the RepositoryFactory to access its repositories. This is very straightforward, and this is useful for many simple applications because the application will then own the ModeShape instance(s).

Web applications are a different story. Often, you would rather your web application not contain the code that initializes the JCR repository, but instead configure ModeShape as a central, shared service that all of your web applications can simply reference and use.

Unfortunately, there's not single way to deploy ModeShape into any web or application server, since they all have slightly different deployment and configuration techniques. The remainder of this section will talk about how to deploy ModeShape to two popular open source servers.

The JBoss Application Server (or JBoss AS) is a very popular open source Java application server, with an extremely healthy and active community. ModeShape offers a way to deploy ModeShape into JBoss AS as as a central, shared service that can be monitored and administered using the embedded console.

ModeShape provides a downloadable ZIP file that can be unzipped into any JBoss AS profile. When you do this, that profile will contain all the files necessary for ModeShape to run when the server is started. The default configuration is for a single, in-memory repository with two users. However, other than basic playing, you will want to edit the configuration files to define a more robust, persistent and secure configuration.

This JBoss AS distribution ZIP file contains several components:

  • JAR files for the JCR 2.0 API and ModeShape's small extensions to the JCR API on the global classpath (that is, in the "lib/" directory). These APIs are available to all deployed applications, services and components. The JCR API contains the "javax.jcr" packages and has no other dependencies. ModeShape's extensions define interfaces in the "org.modeshape.jcr.api" packages; these extend a few of the standard JCR API interfaces and add several methods to make them more useful.

  • The ModeShape Service, represented as an exploded JAR file in the "deploy" directory. This is where the JcrEngine is running, though any application (or other JBoss service) can access its JCR Repository instances using the standard RepositoryFactory approach (covered in the next chapter) with JNDI URLs:

     jndi:jcr/local?repositoryName=repository

    By default, there is a single in-memory repository named "repository", but this can be changed by simply editing the "deploy/modeshape-services.jar/managedConfigRepository.xml" configuration file. All of ModeShape's standard sequencers and connectors (and JARs for their dependencies) are included, meaning they can be configured for use without worrying about adding JARs to the classpath. Feel free to remove any of the JARs are not needed for your custom configuration.

  • A pair of JAAS properties files, located in the "conf/props/" directory, that come out of the box with an "admin" user (with password "admin") that has full read, write, and administration privileges, and a "guest" user (with password "guest") that has only read and write privileges. Simply edit these files to change users, passwords, and roles, or to configure JAAS differently.

  • The ModeShape RESTful API, represented as an exploded WAR file in the "deploy" directory. This allows remote applications to interact with ModeShape to access and manipulate repository content using a RESTful API that uses JSON in the requests and responses. All ModeShape repositories can be accessed, and authentication is done using the ModeShape JAAS configuration.

  • The ModeShape WebDAV API, represented as an exploded WAR file in the "deploy" directory. This web application allows external clients to access and manipulate the content in the ModeShape repositories using the standard WebDAV protocol. For example, you can mount a repository (or parts of it) as a network drive on most operating systems, and then upload or download files and folders using standard OS operations and graphical tools. All ModeShape repositories can be accessed, and authentication is done using the ModeShape JAAS configuration.

  • A plugin for the embedded JBoss AS console, represented as a WAR file in the "deploy" directory. This plugin also works with RHQ/ JOPR administration, monitoring, alerting, operational control and configuration system. (We plan to add more metrics and operations over the next few releases, as we gain more experience using the ModeShape JOPR plugin.)

  • A JDBC driver for querying the repositories through JDBC. This driver is on the global classpath so it can be used in any deployed component. A single JDBC DataSource is also configured in the "deploy/modeshape-services.jar/modeshape-jdbc-ds.xml" file to use the single default in-memory repository available out of the box. Simply edit this file to add or change the DataSource definitions. The driver can also be used in a separate JVM to issue queries and access database metadata.

Here are the contents of this file:

conf/
conf/props/
conf/props/modeshape-roles.properties  
conf/props/modeshape-users.properties  
lib/
lib/jcr-2.0.jar         
lib/modeshape-jcr-api-2.2.0.Final.jar  
lib/modeshape-jdbc-2.2.0.Final.jar  
deploy/
deploy/modeshape-jboss-beans.xml  
deploy/modeshape-services.jar/
deploy/modeshape-services.jar/META-INF/
deploy/modeshape-services.jar/aperture-1.1.0.Beta1.jar 
deploy/modeshape-services.jar/google-collections-1.0.jar  
deploy/modeshape-services.jar/joda-time-1.6.jar  
deploy/modeshape-services.jar/lucene-analyzers-3.0.0.jar  
deploy/modeshape-services.jar/lucene-core-3.0.0.jar  
deploy/modeshape-services.jar/lucene-regex-3.0.0.jar  
deploy/modeshape-services.jar/lucene-snowball-3.0.0.jar  
deploy/modeshape-services.jar/poi-3.6.jar  
deploy/modeshape-services.jar/poi-scratchpad-3.6.jar  
deploy/modeshape-services.jar/managedConfigRepository.xml  
deploy/modeshape-services.jar/rdf2go.api-4.6.2.jar
deploy/modeshape-services.jar/META-INF/jboss-beans.xml  
deploy/modeshape-services.jar/modeshape-cnd-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-common-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-filesystem-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-infinispan-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-jbosscache-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-jcr-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-jdbc-metadata-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-store-jpa-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-connector-svn-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-graph-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-jbossas-service-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-jcr-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-jdbc-ds.xml  
deploy/modeshape-services.jar/modeshape-mimetype-detector-aperture-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-repository-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-search-lucene-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-classfile-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-cnd-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-ddl-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-java-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-jbpm-jpdl-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-msoffice-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-teiid-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-text-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-xml-2.2.0.Final.jar  
deploy/modeshape-services.jar/modeshape-sequencer-zip-2.2.0.Final.jar  
deploy/modeshape-rest.war/
deploy/modeshape-rest.war/META-INF/
deploy/modeshape-rest.war/WEB-INF/
deploy/modeshape-rest.war/WEB-INF/lib/
deploy/modeshape-rest.war/META-INF/MANIFEST.MF  
deploy/modeshape-rest.war/WEB-INF/jboss-web.xml  
deploy/modeshape-rest.war/WEB-INF/lib/jaxrs-api-1.2.1.GA.jar  
deploy/modeshape-rest.war/WEB-INF/lib/jettison-1.1.jar  
deploy/modeshape-rest.war/WEB-INF/lib/modeshape-jcr-2.2.0.Final.jar  
deploy/modeshape-rest.war/WEB-INF/lib/modeshape-web-jcr-2.2.0.Final.jar  
deploy/modeshape-rest.war/WEB-INF/lib/modeshape-web-jcr-rest-2.2.0.Final.jar  
deploy/modeshape-rest.war/WEB-INF/lib/resteasy-jaxb-provider-1.2.1.GA.jar  
deploy/modeshape-rest.war/WEB-INF/lib/resteasy-jaxrs-1.2.1.GA.jar  
deploy/modeshape-rest.war/WEB-INF/lib/resteasy-jettison-provider-1.2.1.GA.jar  
deploy/modeshape-rest.war/WEB-INF/lib/scannotation-1.0.2.jar  
deploy/modeshape-rest.war/WEB-INF/web.xml  
deploy/modeshape-webdav.war/
deploy/modeshape-webdav.war/WEB-INF/
deploy/modeshape-webdav.war/WEB-INF/lib/
deploy/modeshape-webdav.war/WEB-INF/jboss-web.xml  
deploy/modeshape-webdav.war/WEB-INF/lib/aperture-1.1.0.Beta1.jar  
deploy/modeshape-webdav.war/WEB-INF/lib/modeshape-jcr-2.2.0.Final.jar  
deploy/modeshape-webdav.war/WEB-INF/lib/modeshape-mimetype-detector-aperture-2.2.0.Final.jar  
deploy/modeshape-webdav.war/WEB-INF/lib/modeshape-web-jcr-2.2.0.Final.jar  
deploy/modeshape-webdav.war/WEB-INF/lib/modeshape-web-jcr-webdav-2.2.0.Final.jar  
deploy/modeshape-webdav.war/WEB-INF/lib/webdav-servlet-2.0.jar  
deploy/modeshape-webdav.war/WEB-INF/web.xml  
deploy/admin-console.war/
deploy/admin-console.war/plugins/
deploy/admin-console.war/plugins/modeshape-jbossas-console-2.2.0.Final.jar  

Your web application or JBoss service can use one of the JCR Repository instances running inside the ModeShape service by simply using the RepositoryFactory technique described earlier, with a URL such as:

 jndi:jcr/local?repositoryName=repository

Be sure to use the correct repository name.

Since the JCR API JAR is on the global classpath, your web application can use the JCR API without having to include the JAR file in your application's WAR file. In fact, your application will likely get ClassCastExceptions if it does include the JCR API in its WAR file. Plus, if needed, your application can use ModeShape's "org.modeshape.jcr.api" extensions to the JCR API (again, on the global classpath), and should not need or use any of the classes or interfaces in the ModeShape implementation.

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 resource, which must be available on the classpath

  • repositoryName is the name of a JCR repository that exists in the JCR configuration 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. For more details, see the Reference Guide.

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 by using JNDI directly (or the more standard RepositoryFactory technique shown in the next chapter), 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
    IDTrustConfiguration idtrustConfig = new IDTrustConfiguration();
    try {
        idtrustConfig.config("security/jaas.conf.xml");
    } 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.)

Note

If you deploy your application to JBoss AS or EAP and deploy ModeShape as a service, your application doesn't have to do anything with JAAS, since that's provided by the platform.

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.2.0.Final</version>
</dependency>

This adds only the minimal libraries required to use ModeShape. If your application is going to use clustering, you'll need to also depend upon the clustering module:


<dependency>
  <groupId>org.modeshape</groupId>
  <artifactId>modeshape-clustering</artifactId>
  <version>2.2.0.Final</version>
</dependency>

You also 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.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-ddl</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshapce</groupid>
  <artifactid>modeshape-sequencer-images</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-classfile</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-java</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-mp3</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-msoffice</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-xml</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-teiid</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-text</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-sequencer-zip</artifactid>
  <version>2.2.0.Final</version>
</dependency>

Here is the list of available connectors:


<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-filesystem</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-infinispan</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-jcr</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-jbosscache</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-jdbc-metadata</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-store-jpa</artifactid>
  <version>2.2.0.Final</version>
</dependency>
<dependency>
  <groupid>org.modeshape</groupid>
  <artifactid>modeshape-connector-svn</artifactid>
  <version>2.2.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.2.0.Final-jar-with-dependencies.jar contains all of the classes (except those under javax.jcr) necessary to run the core ModeShape JCR repository engine using the in-memory connector and the federating connector;

  • one modeshape-connector-<type>-2.2.0.Final-jar-with-dependencies.jar for each type of connector, each containing all of the classes necessary for that connector, designed to be added to the classpath after the modeshape-jcr-2.2.0.Final-jar-with-dependencies.jar file;

  • one modeshape-sequencer-<type>-2.2.0.Final-jar-with-dependencies.jar for each type of connector, each containing all of the classes necessary for that sequencer, designed to be added to the classpath after the modeshape-jcr-2.2.0.Final-jar-with-dependencies.jar file;

  • modeshape-mimetype-detector-aperture-2.2.0.Final-jar-with-dependencies.jar containing all of the classes necessary for detecting the MIME type of files based upon their name and/or content, designed to be added to the classpath after the modeshape-jcr-2.2.0.Final-jar-with-dependencies.jar file;

  • modeshape-jpa-ddl-gen-2.2.0.Final-jar-with-dependencies.jar contains all of the classes required to run the DDL generation utility as a standalone application.

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.