Red Hat Training

A Red Hat training course is available for Red Hat JBoss Data Virtualization

4.2. Using Repositories with JCR API

4.2.1. JCR API

The JCR API is a powerful and easy way to access or manipulate repository content from within a deployed web application or service. The hierarchical database makes using the JCR API very easy. You can get a javax.jcr.Repository object that represents one of the repositories running within the hierarchical database subsystem and start using the API.

4.2.2. Find the JCR Repository

You can use any of the following ways to find repository instances within JBoss EAP:
  • Use Java EE resource injection
  • Look up a Repository in JNDI
  • Look up the hierarchical database's Repositories instance and use it to find the Repository by name
  • Use JCR's javax.jcr.RepositoryFactory and the Service Loader
These methods rely upon JNDI and the fact that the hierarchical database registers itself and each of the Repository instances into JNDI. The hierarchical database engine, which implements the org.modeshape.jcr.api.Repositories interface, is registered at jcr, while each repository is registered at jcr/{repositoryName}. You can optionally specify an additional JNDI location in the repository configuration. It is useful when you deploy an application that is already looking up a Repository instance at a specific JNDI name that can not be easily changed. For example, for a repository named "sample", the hierarchical database engine automatically registers it into JNDI (in the global context) at jcr/sample, although java:jcr/sample also works in JBoss EAP.

4.2.3. Use Java EE Resource Injection

JBoss EAP is a Java EE compliant application server, which means that your code can use Java EE resource injection to automatically get a reference to the Repository instance. Here is a snippet from a ManagedBean example that has the "sample" repository injected automatically:
@ManagedBean
public class MyBean {
  @Resource(mappedName="java:/jcr/sample")
  private javax.jcr.Repository repository;
 
  ...
}
When you deploy your application, JBoss EAP automatically starts the "sample" repository. When you undeploy your application, JBoss EAP automatically stops the "sample" repository unless there are other applications or subsystems using it. This works because the JBoss EAP deployer encounters the @Resource annotation and automatically adds a dependency for the application on the JNDI binding service associated with the specified JNDI name, which depends upon relevant Repository instance.

4.2.4. Get a Repository Instance from JNDI

You can get a repository by directly looking up the repository in JNDI as shown below:
InitialContext context = new InitialContext();
javax.jcr.Repository repository = (javax.jcr.Repository) context.lookup("jcr/sample");
Consider using this approach if you deploy your application to multiple containers including some non-EE containers.

4.2.5. Use RepositoryFactory of JCR

Not all deployment environments have JNDI support. The JCR 2.0 specification defines a pattern that uses the Java SE Service Loader facility to find javax.jcr.RepositoryFactory instances and use them to get your repository instance. This mechanism also works with for JBoss EAP. If your components that use JCR, are deployed or reused in other applications that are deployed to environments having no JNDI or Java EE support, you can consider this way to look up JCR repositories:

String configUrl = "jndi:jcr/sample";
Map<String, String> parameters = java.util.Collections.singletonMap("org.modeshape.jcr.URL", configUrl);
javax.jcr.Repository repository = null;
for (RepositoryFactory factory : java.util.ServiceLoader.load(RepositoryFactory.class)) {
    repository = factory.getRepository(parameters);
    if (repository != null) break;
}
The RepositoryFactory implementations look for a single org.modeshape.jcr.URL parameter that should be a URL of the form "jndi:jndiName". As your "sample" repository is registered into JNDI at jcr/sample, you can use jndi:jcr/sample for the URL.

4.2.6. Use a Repositories Container

Sometimes your applications may need to do more than look up repository instances. For example, your application may need to know which repositories exist. The hierarchical database provides an implementation of the org.modeshape.jcr.api.Repositories interface that defines several useful methods:

public interface Repositories {
 
    /**
     * Get the names of the available repositories.
     *
     * @return the immutable set of repository names provided by this server; never null
     */
    Set<String> getRepositoryNames();
 
    /**
     * Return the JCR Repository with the supplied name.
     *
     * @param repositoryName the name of the repository to return; may not be null
     * @return the repository with the given name; never null
     * @throws javax.jcr.RepositoryException if no repository exists with the given name or there is an error communicating with
     *         the repository
     */
    javax.jcr.Repository getRepository( String repositoryName ) throws javax.jcr.RepositoryException;
}
The getRepositoryNames() method returns an immutable set of names of all existing repositories, while the getRepository(String) method obtains the JCR repository with the specified name.
The hierarchical database always registers the implementation of this interface in JNDI at the "jcr" (or "java:jcr") name. The following code shows how to directly look up a repository named "sample" using this interface:

InitialContext context = new InitialContext();
Repositories repositories = (Repositories) context.lookup("jcr");
javax.jcr.Repository repository = repositories.get("sample");
You can also use the repositories object with the RepositoryFactory-style mechanism. In this case, the URL should contain jndi:jcr?repositoryName=repositoryName. Here is how you can find the "sample" repository using this technique:

String configUrl = "jndi:jcr/sample";
Map<String, String> params = new HashMap<String, String>();
params.put(org.modeshape.jcr.api.RepositoryFactory.URL, "jndi:jcr?repositoryName=sample");
javax.jcr.Repository repository = null;
for (RepositoryFactory factory : java.util.ServiceLoader.load(RepositoryFactory.class)) {
    repository = factory.getRepository(parameters);
    if (repository != null) break;
}
Here is how you can do the same, separating the URL and repository name:

String configUrl = "jndi:jcr/sample";
Map<String, String> params = new HashMap<String, String>();
params.put(org.modeshape.jcr.api.RepositoryFactory.URL, "jndi:jcr");
params.put(org.modeshape.jcr.api.RepositoryFactory.REPOSITORY_NAME, "sample");
javax.jcr.Repository repository = null;
for (RepositoryFactory factory : java.util.ServiceLoader.load(RepositoryFactory.class)) {
    repository = factory.getRepository(parameters);
    if (repository != null) break;
}
Here is how you can use resource-injection:

@ManagedBean
public class MyBean {
  @Resource(mappedName="java:/jcr")
  private org.modeshape.jcr.api.Repositories repositories;
 
  ...
}

4.2.7. Deploy JCR Web Applications

The modular classloading system in JBoss EAP enables your application to only see those Java APIs that your applications use. As the JCR API is not one of the standard JEE APIs, your application needs to explicitly state that it needs the JCR API and optionally the hierarchical database API. You can manually specify the modules that your application uses in any of the following ways:
  • Specify dependencies in your MANIFEST.MF file.
  • Override dependencies with the jboss-deployment-structure.xml file.

4.2.8. Specify Dependencies with MANIFEST.MF

You can specify dependencies in your application's META-INF/MANIFEST.MF file by adding the following line:
Dependencies: javax.jcr, org.modeshape.jcr.api export services, org.modeshape export services
Adding this line gives your application visibility to the standard JCR API and to the hierarchical database public API. It also ensures that the hierarchical database service is running by the time your application needs it. It also exports any services such as RepositoryFactory implementations, so that the ServiceLoader can find them.
If you modify theMANIFEST.MF file, ensure that you include a newline character at the end of the file.

4.2.9. Override Dependencies with jboss-deployment-structure.xml

The jboss-deployment-structure.xml file is a JBoss specific deployment descriptor. You can use it to control class loading in a fine grained manner. Like the MANIFEST.MF file, you can use this file to add dependencies. This file can also prevent automatic dependencies from being added, define additional modules, change an EAR deployment's isolated class loading behavior, and add additional resource roots to a module. Here is a snippet of the jboss-deployment-structure.xml file:

<jboss-deployment-structure>
  ...
  <deployment>
    ...
    <dependencies>
      ...
      <!-- These are equivalent to the "Dependencies: javax.jcr ..." line in the MANIFEST.MF -->
      <module name="javax.jcr" />
      <module name="org.modeshape.jcr.api" services="import" />
      <module name="org.modeshape" services="import" />
      ...
    </dependencies>
    ...
  </deployment>
  ...
</jboss-deployment-structure>

4.2.10. Build an Application with Maven

As the hierarchical database and JBoss EAP are built with Maven, we recommend you to use Maven to build and test your application.

Procedure 4.11. Task

  1. To build an application with Maven, include the hierarchical database as a provided dependency in your application's POM file. You can do this for each of the artifacts you need, however it is easier to use the hierarchical database's BOM in your <dependencyManagement> section. The example below shows how the POM file specifies the BOM in the dependencyManagement section and how you can specify the Java EE 6 APIs:
    
    <project ...>
        <!-- ... -->
        <dependencyManagement>
            <dependencies>
                <!-- Define the version of JBoss' Java EE 6 APIs we want to import.
                      Any dependencies from org.jboss.spec will have their version defined by this
                      BOM -->
                <!-- JBoss distributes a complete set of Java EE 6 APIs including
                      a Bill of Materials (BOM). A BOM specifies the versions of a "stack" (or
                      a collection) of artifacts. We use this here so that we always get the correct
                      versions of artifacts. Here we use the jboss-javaee-6.0-with-tools stack
                      (you can read this as the JBoss stack of the Java EE 6 APIs, with some extras
                      tools for your project, such as Arquillian for testing) -->
                <dependency>
                    <groupId>org.jboss.bom</groupId>
                    <artifactId>jboss-javaee-6.0-with-tools</artifactId>
                    <version>1.0.0.M11</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
                <!-- Import the ModeShape BOM for embedded usage. This adds to the "dependenciesManagement" section
                     defaults for all of the modules we might need, but we still have to include in the
                     "dependencies" section the modules we DO need. The benefit is that we don't have to
                     specify the versions of any of those modules.-->
                <dependency>
                    <groupId>org.modeshape.bom</groupId>
                    <artifactId>modeshape-bom-jbosseap</artifactId>
                    <version>3.2.0.Final</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <!-- ... -->
    </project>
    
  2. Specify the version that you want to use in the following line:
    
    <version>3.2.0.Final</version>
    
    As the modeshape-bom-jbossas is a BOM, it includes default "version" and "scope" values for all of the hierarchical database artifacts and (transitive) dependencies.
  3. The BOMs add default values for several Java EE6 and hierarchical database artifacts and dependencies, respectively. To make them available in your application, add dependencies for all the artifacts that you directly use. In the case of the hierarchical database, the following is the JCR API and the database's public API:
    
    <dependencies>
        ...
        <!-- Directly depend on the JCR 2.0 API -->
        <dependency>
          <groupId>javax.jcr</groupId>
          <artifactId>jcr</artifactId>
        </dependency>
        <!-- Directly depend on ModeShape's public API -->
        <dependency>
          <groupId>org.modeshape</groupId>
          <artifactId>modeshape-jcr-api</artifactId>
        </dependency>
        ...
      </dependencies>
    
    Here you do not have to specify any versions or scope of these artifacts because they are specified in the BOMs used in the <dependencyManagement> section. In the case of these two artifacts, the default scope is "provided", which means that Maven makes them available for compilation. However, since they are provided by JBoss EAP, the hierarchical database runtime environment does not include them in any produced artifacts like WAR files.

Warning

Your deployable web applications and services need not contain any of the JARs from the hierarchical database, JCR API, Infinispan, Hibernate Search, Lucene, Joda Time, Tika, or any of the other libraries that the database uses. Doing so will result in (convoluted) deployment errors regarding class incompatibilities or class cast exceptions. If your code directly uses these libraries, add them as a dependency in your MANIFEST.mf file or jboss-deployment-structure.xml file.

4.2.11. Build an Application with Non-Maven Tools

If you are using a non-Maven tool to build your application, ensure that the resulting deployments such as WAR and EAR files do not contain any of the JARs from the hierarchical database, JCR API, Infinispan, Hibernate Search, Lucene, Joda Time, or any of the other libraries that the database uses. These are provided by the subsystem in JBoss EAP. If your code uses any of these, add them as a dependency in your MANIFEST.mf file or jboss-deployment-structure.xml file.