Mule 1.x JCR Transport User Guide
The current version of the transport connects to JCR 1.0 (aka JSR 170) repositories.
The javadoc for this transport provider can be found here. And the Source Xref can be found here.
Configuration
The following table shows the different properties understood by this transport. Except the ones whose names are rendered in bold, these properties can be set at connector, endpoint or message properties levels. The configuration samples shown further down this page will make these options clearer.
| Property |
Description |
Default |
Required |
repository |
A reference to the javax.jcr.Repository to which this connector will listen. |
|
Yes |
| username |
If the repository requires authentication, the username to use when opening a session. |
|
No |
| password |
If the repository requires authentication, the password to use when opening a session. |
|
Yes (if username is provided) |
workspaceName |
The name of the workspace to use for the session. If not provided, a default workspace is automatically selected by the repository implementation. |
|
No |
contentPayloadType |
When an event of type PROPERTY_ADDED or PROPERTY_CHANGED is received, the connector can try to get the content associated with the property that was source of the event and add it to the Mule Message payload.
Why try? If the property is removed after the event is received but before the connector tries to retrieve the content, then no content will be included in the payload.
This property controls what content will be added to the payload. The valid values are: NONE, NOBINARY and FULL.
The latter includes binary properties content in the payload, which might be heavy hence not always desirable. Binary content is stored as a byte[]. |
NONE |
No |
| eventTypes |
A combination of one or more event type constants encoded as a bitmask, as defined by the JCR API. |
0 |
No |
deep |
Define if events in the subtree of the path defined on the endpoint will also be received. |
true |
No |
uuid |
Only events whose associated node has one of the UUIDs in this list will be received. |
|
No |
| nodeTypeName |
Defines the node type that this connector will listen to or that the endpoint will create. |
|
No |
noLocal |
If noLocal is true, then events generated by the session through which the listener was registered are ignored. |
false |
No |
nodeRelPath |
Defines a node relative path from the one defined in the endpoint or nodeUUID. It understands the following patterns:
- ${DATE} - the current date in the format dd-MM-yy_HH-mm-ss.SS
- ${DATE:yy-MM-dd} - the current date using the specified format
- ${SYSTIME} - the current system time milliseconds
- ${UUID} - a generated universally unique id
- ${property name} - a property name that will beexhaustively searchedin the current event.
|
|
No |
| propertyRelPath |
Defines a property relative path from the one defined in the endpoint or nodeUUID, combined with an optional nodeRelPath. It understands the same patterns as nodeRelPath. |
|
No |
| nodeUUID |
Defines a single node by its UUID. |
|
No |
queryStatement |
Defines a query that will be executed to retrieve a single node. |
|
No |
queryLanguage |
Specifies the language used in the query statement (for example 'xpath' or 'sql'). |
|
Yes (if queryStatement is provided) |
| alwaysCreate |
Forces the transport to create a new node even if one exists under the path specified by the endpoint and the optional nodeRelPath. |
|
No |
Connecting to the Repository
The JSR 170 does not mandate a unique way of getting a repository instance hence it must be instantiated and passed to the connector. An easy way to do so is to build the repository instance in Spring and inject it in the connector.
Here is a Spring configuration file that instantiates a JackRabbit RMI client:
<beans>
<bean id="rmiClientFactory" class="org.apache.jackrabbit.rmi.client.ClientRepositoryFactory" />
<bean id="jcrRepository" factory-bean="rmiClientFactory"
factory-method="getRepository">
<constructor-arg value="rmi: />
</bean>
</beans>
And here is how to inject the repository in the connector in the Mule configuration:
<container-context className="org.mule.extras.spring.SpringContainerContext">
<properties>
<property name="configFile" value="../conf/spring-beans.xml"/>
</properties>
</container-context>
<connector name="JcrConnector" className="org.mule.providers.jcr.JcrConnector">
<properties>
<container-property name="repository" reference="jcrRepository" required="true"/>
</properties>
</connector>
Overriding or setting connector properties on the endpoint
It is possible to set the different properties on a JCR endpoint: if the property was defined on the connector, it will be overriden by the one set on the endpoint.
For example, the following sets the contentPayloadType property:
while this example shows how to set an uuid list on the endpoint:
<endpoint address="jcr: connector="JcrConnector">
<properties>
<list name="uuid">
<entry value="uuid-01"/>
<entry value="uuid-02"/>
</list>
</properties>
</endpoint>
Usage
Listening to the events under a particular path
JCR observations mainly consist in listening to changes from a particular node (and all its children in "deep" mode). The path of the observed node must be specified on the endpoint, as well as the connector used to reach the repository.
For example the following endpoint configuration:
<endpoint address="jcr: connector="JcrConnector"/>
will observe a node whose absolute path is /path/to/node and will use the specified connector for reaching the repository.
Retrieving data from an arbitrary path
The transport receives JCR content from the configured endpoint, using optional event properties to define the target repository item. Unless an exception is thrown, a UMOMessage will always be retrieved, possibly with a null payload if no content was acessible.
The content is extracted from the property or properties that were targeted by the endpoint path, filters and event optional parameters.
The first step of the content fetching consists in selecting a target item from the repository. By default, this item is a node selected by using the path of the endpoint. Alternatively, an event property (nodeUUID) can be used to specify the UUID to use to select the target node: if this is done, the endpoint URI will be ignored. A third option is to define a query (with queryStatement and queryLanguage) that will be used to select the node. Then, if any optional relative paths have been specified as event properties for the current UMOEvent (nodeRelPath and/or propertyRelPath), they will be used to navigate to the actual item to use.
The second step consists in applying any JcrNodeNameFilter or JcrPropertyNameFilter that could have been defined on the endpoint to further narrow the target item from which content will be extracted. If more than one node is selected, the first one will be arbitrarily used as the target item and a warning will be issued. If no item can be selected, a null payload will be used for the returned UMOMessage.
The final step is the content extraction that will be used as the UMOMessage payload. For this, the following rules apply, depending on the target item:
- For a single-valued property, the payload will be the property value.
- For a multi-valued property, the payload will be a List of values.
- For a node, the payload will be a Map of property names and property values (for these values, the previous two rules will apply).
The following configuration shows an HTTP endpoint that fetches data from a JCR container, turning Mule into a (bare bone) HTTP picture server. Note how:
- The GIF image is extracted from the target node whose known type nt:file dictates the jcr:content and jcr:data children item structure,
- This structure is expressed as filters on the global endpoint,
- The component uses the nodeRelPath property to pass the image name as the name of a node child of the path defined in the global endpoint.
<global-endpoints>
<endpoint name="jcrImages" address="jcr:>
<filter className="org.mule.routing.filters.logic.AndFilter">
<left-filter className="org.mule.providers.jcr.filters.JcrNodeNameFilter" pattern="jcr:content" />
<right-filter className="org.mule.providers.jcr.filters.JcrPropertyNameFilter" pattern="jcr:data" />
</filter>
</endpoint>
</global-endpoints>
<mule-descriptor name="jcrHttpContentFetcher" implementation="org.mule.components.script.jsr223.ScriptComponent">
<inbound-router>
<endpoint address="http: synchronous="true" />
</inbound-router>
<properties>
<property name="scriptEngineName" value="groovy" />
<text-property name="scriptText">
def imageName = message.getPayload().substring(8);
message.clearProperties();
message.setStringProperty("nodeRelPath", imageName);
message.setStringProperty("Content-Type", "image/gif");
eventContext.setStopFurtherProcessing(true);
return managementContext.lookupEndpoint("jcrImages").receive(0).getPayload();
</text-property>
</properties>
</mule-descriptor>
Browsing http://localhost:8080/images/image_name.gif should display an image in your favorite browser (provided there is one stored in the repository!).
Storing content
It is possible to store content to the configured JCR endpoint, using optional event properties to define the target repository item and the node type name to use.
Unless the creation of child nodes is forced by an event or endpoint property (alwaysCreate), the target item where content will be stored will determined the same way it is done for reading content.
If an existing target item is found and is a node, the appropriate NodeTypeHandler will be used to convert the UMOMessage payload into valid JCR content (nodes and properties).
If an existing target item is found and is a property, the UMOMessage payload will be directly written to it, using a simple conversion mechanism. Note that if the payload is a Collection, the property will be multi-valued.
If no existing target item is found or if the creation of a new node is forced (see first paragraph), a new node will be created, under the absolute path defined by the endpoint URI, with a content extracted from the UMOMessage payload and stored according to the type defined in the event or connector property (nodeTypeName). If the endpoint URI points to a property and not a node, an exception will be raised.
Example of a endpoint that receives data and saves it as a node of type nt:resource that is auto-created the first time, then updated.
<mule-descriptor name="jcrStoreToSingleNode" implementation="org.mule.components.simple.BridgeComponent"
inboundEndpoint="vm:>
<outbound-router>
<router className="org.mule.routing.outbound.OutboundPassThroughRouter">
<endpoint address="jcr:>
<properties>
<property name="nodeRelPath" value="targetSingleNtResourceNode" />
<property name="nodeTypeName" value="nt:resource" />
<property name="jcr:mimeType" value="text/plain" />
</properties>
</endpoint>
</router>
</outbound-router>
</mule-descriptor
Example of a endpoint that receives data and saves it as a new child node of type nt:unstructured.
<mule-descriptor name="jcrStoreToNewChildNode" implementation="org.mule.components.simple.BridgeComponent"
inboundEndpoint="vm:>
<outbound-router>
<router className="org.mule.routing.outbound.OutboundPassThroughRouter">
<endpoint address="jcr:>
<properties>
<property name="alwaysCreate" value="true" />
<property name="nodeRelPath" value="targetMultipleUnstructuredNode" />
<property name="nodeTypeName" value="nt:unstructured" />
</properties>
</endpoint>
</router>
</outbound-router>
</mule-descriptor>
Registering a custom node type handler
To support the particular storage organization (properties and child nodes) of specific node types, the transport uses node type handlers registered to a manager that is accessible from the connector. The transport comes with a few handlers for the main node types and supports the registration of custom ones for user defined or any other extra types.
The following example demonstrates the registration of a custom type handler for "nt:query".
jcrConnector.getNodeTypeHandlerManager().registerHandler(
new NodeTypeHandler() {
public String getNodeTypeName() {
return "nt:query";
}
public Node newNode(Session session, Node targetNode, String nodeRelPath, UMOMessage message) throws RepositoryException, IOException {
Node node = targetNode.addNode(nodeRelPath, getNodeTypeName());
storeContent(session, node, message);
return node;
}
public void storeContent(Session session, Node node, UMOMessage message) throws RepositoryException, IOException {
node.setProperty("jcr:statement", message.getStringProperty("jcr:statement", null));
node.setProperty("jcr:language", message.getStringProperty("jcr:language", null));
}
});
Bear in mind that you do not need to have an actual node type declared in your JCR container: the transport allows you to enforce a defined data structure without it. If you need guidance on how to store content in JCR, better learn from the master!
Transactions
Not currently supported by the transport.
Streaming
Not currently supported by the transport.
Transformers
The transport uses two default transformers, as explained in the following table.
| Router |
Transformer |
Description |
| Inbound |
org.mule.providers.jcr.transformers.JcrEventToObject |
A single observation event on a JCR repository contains in fact an iterator of javax.jcr.observation.Event objects: this transformer transforms this iterator in a Collection of org.mule.providers.jcr.JcrMessage objects, designed to be detached from the container and serializable. |
Response |
org.mule.providers.jcr.transformers.JcrItemToObject |
A receive operation on the connector returns a single JCR Item of type Node or Property. This transformer extracts the content from the Item and stores it:
- in a single object if the Item is a single value property,
- in a list of objects if the Item is a multi-valued property,
- in a map of property names and property values (objects or lists) if the Item is a node.
|
You can override these transformers with the NoActionTransformer if you desire to handle JCR objects in your components.