JBoss Community Archive (Read Only)

ModeShape 5

Using Repositories with JCR API in Wildfly

The JCR API is powerful and easy way to access or manipulate repository content from within a deployed web application or service. And the ModeShape kit for Wildfly makes that trivially easy: simply get a javax.jcr.Repository object that represents one of the repositories running within the ModeShape subsystem, and start using the API.

Finding the JCR Repository

There are four ways to find repository instances within Wildfly, and which you use is up to you:

  • Using Java EE resource injection

  • Look up a Repository in JNDI

  • Look up ModeShape's Repositories instance and use it to find the Repository by name

  • Use JCR's javax.jcr.RepositoryFactory and the Service Loader

All of these methods rely upon JNDI and the fact that ModeShape registers itself and each of the Repository instances into JNDI. The ModeShape engine (which implements the "org.modeshape.jcr.api.Repositories interface) is registered at "jcr", while each repository is registered at "jcr/{repositoryName}". Note that an additional JNDI location can be specified in the repository configuration; this is optional but useful when your deploying an application that is already looking up a Repository instance at a specific JNDI name that cannot (easily) be changed.

Consider a repository named "sample". ModeShape automatically registers it into JNDI (in the global context) at "jcr/sample", although "java:jcr/sample" also works in Wildfly.

With this knowledge of how ModeShape uses JNDI, let's look at the different ways of getting a hold of Repository instances.

Use Java EE resource injection

JBoss Wildfly 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's 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 your application is deployed, the server will automatically start the "sample" repository. And when your application is undeployed, the server will automatically stop the "sample" repository (unless there are other applications or subsystems using it). This works because the Wildfly 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 in the case of ModeShape is dependent upon relevant Repository instance.

Using resource injection is by-far the easiest approach.

Get a Repository instance from JNDI

Another very simple approach is to simply look up the Repository instance in JNDI:

InitialContext context = new InitialContext();
javax.jcr.Repository repository = (javax.jcr.Repository) context.lookup("jcr/sample");

Directly looking up the repository in JNDI is perhaps the second easiest way to get a ModeShape repository, and this approach was the idiomatic way to do this in JCR 1.0. But JCR 2.0 added a different approach. You might want to consider using this approach if you deploy your application to multiple containers (including some non-EE containers).

Use JCR's RepositoryFactory

Not all deployment environments have JNDI support. If your components that use JCR are deployed reused in other applications that are deployed to environments that don't have JNDI or Java EE support non-EE environments, you may want to consider the idiomatic way to look up JCR repositories. 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 ModeShape in Wildfly:

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;
}

As shown above, ModeShape's RepositoryFactory implementations look for a single "org.modeshape.jcr.URL" parameter that should be a URL of the form "jndi:jndiName". Since our "sample" repository is registered into JNDI at "jcr/sample", we just use "jndi:jcr/sample" for the URL.

Use ModeShape 'Repositories' container

If your application is just looking up a Repository instance, then the approaches covered above will certainly work and are recommended because they're very simple, straightforward, and depend only on the JCR API. However, sometimes your applications need to do more than just look up Repository instances. For example, perhaps your application needs to know which repositories exist.

For these situations, ModeShape 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.

ModeShape always registers the implementation of this interface in JNDI at the "jcr" (or "java:jcr") name. Therefore, 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");

The Repositories object can also be used with the RepositoryFactory-style mechanism. In this case, the URL should contain "jndi:jcr?repositoryName=repositoryName". For example, the following code finds 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;
}

or, by 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;
}

And finally, you can even use resource-injection:

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

  ...
}

Deploying JCR web applications

The server's modular classloading system means that your application will only see those Java APIs that your applications use. But the JCR API is not one of the standard JEE APIs, so your application needs to explicitly say that it needs the JCR API (and optionally the ModeShape API).

We plan to provide and look for CDI annotations to automatically add in these dependencies for your application. Until then, you have to do it manually.

There are two ways to manually specify the modules that your application uses:

  1. Specify dependencies in your 'MANIFEST.MF' file

  2. Override dependencies with the 'jboss-deployment-structure.xml' file

Specifying dependencies with MANIFEST.MF

Edit your application's 'META-INF/MANIFEST.MF' file and add the following line:

Dependencies: javax.jcr, org.modeshape.jcr.api export services, org.modeshape export services

This line does a couple of things. First, it gives your application visibility to the standard JCR API and to the ModeShape public API. Second, it ensures that the ModeShape service is running by the time your application needs it. And finally, it exports any services (e.g., RepositoryFactory implementations) so that the ServiceLoader can find them. For more detail, see the Wildfly classloading documentation.

If you modify the MANIFEST.MF file, make sure to include a newline character at the end of the file.

Override dependencies with 'jboss-deployment-structure.xml'

This file is a JBoss-specific deployment descriptor that can be used to control class loading in a fine grained manner. Like the MANIFEST.MF, this file can be used to add dependencies. If can also prevent automatic dependencies from being added, define additional modules, change an EAR deployment's isolated class loading behaviour, and add additional resource roots to a module.

<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>

For additional information, see JBoss Deployment Structure File (or for AS7).

Building the application with Maven

Because ModeShape and Wildfly are built with Maven, using Maven to build and test your application is extremely easy and recommended. In your application's POM file, simply include ModeShape as a "provided" dependency. You can do this for each of the artifacts you need, but it is far easier to simply use ModeShape's BOM in your "<dependencyManagement>" section. Here's an example of what this looks like, including how to specify the Java EE APIs:

POM file should specify ModeShape's BOM in the dependencyManagement section
<project ...>
    <!-- ... -->
    <dependencyManagement>
        <dependencies>                      
            <!-- Import the ModeShape BOM. 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-jbossas</artifactId>
                <version>5.0.0.Final</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!-- ... -->
</project>

Specify the ModeShape version that you want to use on line 28. Because the modeshape-bom-jbossas is a BOM, it includes default "version" and "scope" values for all of ModeShape's artifacts and (transitive) dependencies.

Those BOMs essentially add default values for lots of Java EE and ModeShape artifacts and dependencies, respectively. But those are just defaults. To make them available in your application, you must add dependencies for all the artifacts you'll directly use. In the case of ModeShape, this is probably just the JCR API and ModeShape's public API:

POM file should specify ModeShape JARs as 'provided'
<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>

Note here how we don't have to specify any versions or scope of these artifacts? That's because they're 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, but since they are "provided" by the Wildfly+ModeShape runtime environment will not include them in any produced artifacts like WAR files.)

Warning

Your deployable web applications and services should not contain any of the JARs from ModeShape, JCR API, Tika, or any of the other libraries that ModeShape uses. Doing so will result in (convoluted) deployment errors regarding class incompatibilities or class cast exceptions. If your code needs to directly use these libraries, simply add them as a dependency in your MANIFEST.MF file or jboss-deployment-structure.xml file.

Building the application with other tools

If you're using a non-Maven tool to build your application, be sure that the resulting deployments (e.g., WAR and EAR files) do not contain any of the JARs from ModeShape, JCR API or any of the other libraries that ModeShape uses. These are provided by the ModeShape subsystem in Wildfly. If your code needs any of these, simply add them as a dependency in your MANIFEST.mf file or jboss-deployment-structure.xml file.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-11 12:12:44 UTC, last content change 2016-04-05 12:05:29 UTC.