In this recipe, we will see how GateIn communicates with another ECM repository, JBoss ModeShape.
ModeShape is a JCR implementation provided by the JBoss Community. The main feature that makes it stand out is the repository federation.
The federation provides a single JCR interface for accessing and searching contents coming from different backend systems. You might think of a ModeShape repository containing information from a relational database, a filesystem, and perhaps even another Java content repository, for instance, Hippo CMS 7's content repository.
In this recipe, you will see some examples of a portlet that communicates with a ModeShape endpoint.
To follow this recipe, you will need:
ModeShape will be installed automatically through Maven. We will create two portlets, with one making a local connection and the other connecting remotely remotely to the repository to access the content.
In this sample, you will learn how to bootstrap a ModeShape repository at runtime using Maven. Let's look at the required steps:
pom.xml
file as follows:<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>my.gatein</groupId> <artifactId>ModeshapePortlet</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.jcr</groupId> <artifactId>jcr</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-jcr</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-cnd</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-connector-store-jpa</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-connector-filesystem</artifactId> <version>2.8.1.Final</version> </dependency> <dependency> <groupId>org.modeshape</groupId> <artifactId>modeshape-web-jcr-rest-client</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> </dependency> </dependencies> </project>
portlet.xml
, declare the two portlets as follows:<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"> <portlet> <portlet-name>LocalModeshapePortlet</portlet-name> <portlet-class>my.gatein.LocalModeshapePortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> <portlet-info> <title>Modeshape Local Portlet</title> </portlet-info> </portlet> <portlet> <portlet-name>RemoteModeshapePortlet</portlet-name> <portlet-class>my.gatein.RemoteModeshapePortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> <portlet-mode>help</portlet-mode> </supports> <portlet-info> <title>Modeshape Remote Portlet</title> </portlet-info> </portlet> </portlet-app>
my.gatein.LocalModeshapePortlet
. In the following sample, you will create a new repository session in the processAction
method:import org.modeshape.jcr.JcrRepositoryFactory;
import java.util.ServiceLoader;
import javax.portlet.*;
import javax.jcr.*;
...
@Override
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
Properties parameters = new Properties();
parameters.put("org.modeshape.jcr.URL",
"file:/configRepository.xml?repositoryName=Cars");
Repository repository = null;
for (RepositoryFactory factory : ServiceLoader
.load(RepositoryFactory.class)) {
if (factory instanceof JcrRepositoryFactory)
try {
repository = factory.getRepository(parameters);
Session session = (Session) repository.login("workspace1");
Node root = (Node) session.getRootNode();
printAllNodes(root, "");
} catch (RepositoryException e) {
e.printStackTrace();
}
}
}
src/main/resources
folder of your Maven project. The example used is a repository for vehicles; it can be found in the ModeShape 2.8.1 source or by connecting directly to a code search engine. You can download grepcode
from this URL: http://grepcode.com/snapshot/repository.jboss.org/nexus/content/repositories/releases/org.modeshape.examples/modeshape-example-repositories/2.8.1.Final/Copy the following required configuration files:
configRepository.xml
: This is the main configuration file. It declares a federated vehicles repository composed of cars represented by an in-memory repository, airplanes represented by a JPA repository, and UFOs represented by a file-system repository.cars.cnd
: This is the descriptor for the cars. It declares the structure of a JCR car node.aircraft.cnd
: This is the descriptor for the airplanes. It declares the structure of a JCR aircraft node.actionProcess
method of the my.gatein.RemoteModeshapePortlet
:import javax.portlet.*; import javax.jcr.*; import org.modeshape.web.jcr.rest.client.IRestClient; import org.modeshape.web.jcr.rest.client.domain.QueryRow; import org.modeshape.web.jcr.rest.client.domain.Repository; import org.modeshape.web.jcr.rest.client.domain.Server; import org.modeshape.web.jcr.rest.client.domain.Workspace; import org.modeshape.web.jcr.rest.client.json.JsonRestClient; ... @Override public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException { Server server = new Server(REMOTE_SERVER_URL, "username", "password"); Repository repository = new Repository("Cars", server); Workspace workspace = new Workspace("workspace1", repository); IRestClient restClient = new JsonRestClient(); try { List<QueryRow> rows = restClient.query(workspace, "xpath", "//*"); ... } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }
public void printAllNodes(Node root, String space) throws RepositoryException { NodeIterator nodeIterator = (NodeIterator) root.getNodes(); while (nodeIterator.hasNext()) { Node node = (Node) nodeIterator.nextNode(); System.out.println(space + node); System.out.println(); PropertyIterator pi = node.getProperties(); while (pi.hasNext()) { Property property = pi.nextProperty(); System.out .println(space + "------------------------------------------------------"); System.out.print(space + property.getName() + " - "); try { System.out.println(property.getValue().getString()); } catch (ValueFormatException e) { for (Value value : property.getValues()) System.out.print(value.getString() + " - "); System.out.println(); } } printAllNodes(node, space + " "); } }
workspace1
workspace.In the pom.xml
file, we declare enough libraries to execute an interaction with ModeShape. Of course, ModeShape provides several
other plugins that we don't need to show in this recipe.
You can see the standard portlet and JCR API libraries needed for the creation of the portlet and the interaction with a common JCR repository.
The modeshape-jcr
project is the core JCR implementation. The modeshape-cnd
reads the cnd
configuration files used to represent the JCR nodes structure.
If you look at the configRepository.xml
file, you will see the declaration of a federated repository named Vehicles
:
<mode:source jcr:name="Vehicles"> <mode:classname>org.modeshape.graph.connector.federation.FederatedRepositorySource</mode:classname> <mode:workspaces> <mode:workspace jcr:name="virtual"> <mode:projections> <!-- Project the 'Cars' content, starting with the '/Cars' node. --> <mode:projection jcr:name="Cars projection" mode:source="Cars" mode:workspaceName="workspace1"> <mode:projectionRules>/Vehicles/Cars => /Cars</mode:projectionRules> </mode:projection> <!-- Project the 'Aircraft' content, starting with the '/Aircraft' node. --> <mode:projection jcr:name="Aircarft projection" mode:source="Aircraft" mode:workspaceName="workspace2"> <mode:projectionRules>/Vehicles/Aircraft => /Aircraft</mode:projectionRules> </mode:projection> <!-- Project the 'UFOs' content, starting with the root node. --> <mode:projection jcr:name="UFO projection" mode:source="UFOs" mode:workspaceName="workspace1"> <mode:projectionRules>/Vehicles/UFOs => /</mode:projectionRules> </mode:projection> </mode:projections> </mode:workspace> </mode:workspaces> </mode:source>
The Vehicles repository virtualizes all the three repositories: Cars, Aircrafts, and UFOs. The UFOs repository maintains the references in the filesystem through the modeshape-connector-filesystem
project. The Aircraft repository needs the modeshape-connector-jpa
project to be restored through the JPA framework.
The slf4j-log4j12
library is required for ModeShape. Always use the current version used by GateIn, 1.5.8, to avoid confusion between the versions.
In this sample, we are using JCR 2.0 API instead of the 1.0
version used by eXo JCR. This version allows us to use the javax.jcr.RepositoryFactory
class that can be discovered as a service by the java.util.ServiceLoader
class of the Java 6 API.
The ServiceLoader is a utility class provided by Java for loading services as an extension mechanism. A service is a set of interfaces and concrete classes that you need to add features to the application in a clean and transparent way.
This approach allows you to extend any Java application dropping new resources in an external path/package. These external services will be registered as components.
For GateIn, each extension must implement the interface org.gatein.management.spi.ManagementExtension
, and GateIn will look up for extensions under the path /META-INF/services
.
For more information about ServiceLoader, please visit http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html.
This is because the file META-INF/services/javax.jcr.RepositoryFactory
is set in the modeshape-jcr
library, containing the implementation row:
org.modeshape.jcr.JcrRepositoryFactory # ModeShape JCR Implementation
In this manner, if you need to use different JCR repositories, you can use the same client to find them and filter them using similar code:
if (factory instanceof JcrRepositoryFactory) ... else ...
Through the org.modeshape.jcr.URL
property passed to the JCR factory.getRepository(parameters)
operation , the configuration file is read and the working repository is found thanks to the parameter value ?repositoryName=Cars
.
The login operation starts the ModeShape repository. At first login, all objects are created and persisted in the filesystem or in the database according to the configuration of configRepository.xml
.
When the thread ends, the repository also ends.
The remote connection needs the Rest API for ModeShape.
This is installed through the modeshape-web-jcr-rest-client
library declared in the pom.xml
.
The remote connection needs a server configuration for ModeShape. It can be configured together with a webdav server. See Chapter 8, Migrating from Existing Portals for some information on webdav, or see the Red Hat doc for complete information on how to configure webdav in ModeShape:
18.227.79.241