Version 3

    The following is an extract from my notes about getting JackRabbit deployed as a resource adapter in JBoss AS6 and AS7. No guarantees about accuracy of these instructions, but they have been used by a couple of people to get the resource adapter going and so should give enough info to get you going.

     

    The configuration described is using pure JDBC persistence for the repository (no journal files) and using PostgreSQL for the database.

     

     

     

    1. Deploying in JBoss

    JBoss AS6 and JBoss AS7 are quite different when it comes to configuring the datasources and deploying the Jackrabbit resource adapter. The resource adapter (archive) itself should not need to change although there are some minor changes described below. The configuration of the resource adapter, the deployment of the database JDBC driver and the configuration of the database datasources is quite different.

    1.1.  JBoss AS6 Deployment

    1.1.1.  JBoss JCR Module

    You will need to include the jcr API jar file in the jboss class path, the easiest way to do this is to copy the jcr-2.0.jar file to the $(JBOSS_HOME)/common/lib directory. It is available as part of the JCR-283 specification download.

    1.1.2.  Deploy the PostgreSQL JDBC Database Driver

    Copy the jdbc driver jar postgresql-9.0-801.jdbc4.jar to the server/default/lib directory (a peer directory to the deploy directory).

    1.1.3.  Configure the Datasource in JBoss

    Create a PostgreSQL user and DB for JackRabbit:

    1. Create a new user:
    $ POSTGRES_HOME/bin/createuser -d -e -E -P -r -s
    Enter name of role to add: documentstore
    Enter password for new role: xxxxx
    Enter it again: xxxxx
    ## If this doesn't work, try: $POSTGRES_HOME/bin/createuser -d -e -E -P -r -s --username=postgres
    1. Create a Database

    $ POSTGRES_HOME/bin/createdb -e --owner=documentstore --username=documentstore -W documentstore

    The following datasource descriptor should be deployed to the jboss deploy directory ($JBOSS_HOME/server/default/deploy/). It configures a new datasource connnected to a postgreSQL database. The datasource is configured for local transactions only as required by Jackrabbit.

    <?xml version="1.0" encoding="UTF-8"?>
    <datasources>
    <local-tx-datasource>
      <jndi-name>jdbc/DocumentStoreDS</jndi-name>
      <connection-url>jdbc:postgresql://localhost/documentstore</connection-url>
      <driver-class>org.postgresql.Driver</driver-class>
    <user-name>documentstore</user-name>
    <password>xxxxx</password>
      <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
    <exception-sorter-class-name>
      org.jboss.resource.adapter.jdbc.vendor.PostgreSQLExceptionSorter
    </exception-sorter-class-name>
    <!--
      <new-connection-sql>select 1</new-connection-sql>
    -->
    <check-valid-connection-sql>select 1</check-valid-connection-sql>
    <max-pool-size>15</max-pool-size>
    <min-pool-size>3</min-pool-size>
    <prefill>true</prefill>
      <idle-timeout-minutes>15</idle-timeout-minutes>
      <blocking-timeout-millis>60000</blocking-timeout-millis>
      <background-validation>false</background-validation>
      <track-statements>warn</track-statements>
    <metadata>
    <type-mapping>PostgreSQL 8.0</type-mapping>
    </metadata>
    </local-tx-datasource>
    </datasources>

    Table 1 $JBOSS_HOME/server/default/deploy/documentstore-ds.xml

    1.1.4.  Deploy the Jackrabbit Resource Adapter to JBoss

    A slight change (hack) is required to the shipped jackrabbit rar file. It includes a copy of commons-collections, slf4j-api, logback-classic and logback-core that are incompatible with jboss AS6 so we need to unpack the rar, remove the commons-collections jar file and repack it. It is then copied to the server/default/deploy directory.

    1.1.5.  Add the Jackrabbit JCA configuration to JBoss

    Create the following deployment descriptor which will configure the resource adapter contained in the rar file and register it with the jndi name shown. Remember to change the filepath to YOUR filepath.

    <?xml version="1.0" encoding="UTF-8"?>
    <connection-factories>
    <tx-connection-factory>
    <jndi-name>jca/DocumentStore</jndi-name>
    <xa-transaction/>
    <rar-name>jackrabbit-jca-2.2.7.rar</rar-name>
      <connection-definition>javax.jcr.Repository</connection-definition>
    <config-property name="HomeDir" type="java.lang.String">*/Users/bwallis/InfoMedix/JBoss/jboss-6.0.0.Final/server/default/repository*</config-property>
    <config-property name="ConfigFile" type="java.lang.String">*/Users/bwallis/InfoMedix/JBoss/jboss-6.0.0.Final/server/default/repository/repository.xml*</config-property>
    <config-property name="bindSessionToTransaction" type="java.lang.Boolean">true</config-property>
    </tx-connection-factory>
    </connection-factories>

    Table 2 $JBOSS_HOME/server/default/deploy/jcr-ds.xml

    1.2. JBoss AS7 Deployment

    1.2.1. JBoss JCR Module

    You will need to add the jcr API jar to the jboss modules (it isn't currently included with jboss). It is available as part of the JCR-283 specification download. Create a new directory ${JBOSS_HOME)/modules/javax/jcr/main and copy the jcr api jar file into that directory, the file is jcr-2.0.jar (should be in your maven repository at javax/jcr/jcr/2.0). Then you need to create two more files as shown

    META-INF
    META-INF/maven
    META-INF/maven/javax.jcr
    META-INF/maven/javax.jcr/jcr
    javax
    javax/jcr
    javax/jcr/lock
    javax/jcr/nodetype
    javax/jcr/observation
    javax/jcr/query
    javax/jcr/query/qom
    javax/jcr/retention
    javax/jcr/security
    javax/jcr/util
    javax/jcr/version

     

    Table 3 jcr-2.0.jar.index

    <?xml version="1.0" encoding="UTF-8"?>

    <module xmlns="urn:jboss:module:1.0" name="javax.jcr">
      <dependencies>
      <module name="javax.transaction.api" export="true"/>
      </dependencies>

    <resources>
      <resource-root path="jcr-2.0.jar"/>
      <!-- Insert resources here -->
    </resources>
    </module>

    Table 4 module.xml

    Now the jcr api jar file is available to deployed applications that have a dependency on it.

    Next we need to modify the jackrabbit jcr rar file to include the dependency on the jcr module we just defined.

    1.2.2. Deploy the PostgreSQL JDBC Database Driver

    If you don't already have postgres drivers installed in jboss7 then you need to deploy them. This is relatively easy if you have the driver jar file at hand. Just copy it into the $(JBOSS_HOME)/standalone/deployments directory. Make sure you have the latest one for PostgreSQL 9.0 and the JDBC4 compliant one (the JDBC3 one will probably work as well).

    An alternative would be to create a JBoss 7 module in the modules directory (similar to the jcr module described above). Deploying the JDBC jar file seems to be a good solution for now.

    1.2.3. Configure the Datasource in JBoss

    You need to add this to the $JBOSS_HOME)/standalone/configuration/standalone.xml in the datasources section. Note that the document store needs its own datasource configured and should not share the datasource configured for the hibernate persistence or used for the JMS persistence. The reason for this is that jackrabbit implements the XA protocol internally and requires a non-XA datasource underneath rather than relying on the datasource's XA implementation to coordinate commits across multiple providers (in essence, jackrabbit is a persistence provider and as such coordinates its own end of the transactions).

    <datasource jndi-name="jdbc/DocumentStoreDS" pool-name="DocumentStoreDS_Pool" enabled="true"
      jta="true" use-java-context="false" use-ccm="true">
      <connection-url>
      jdbc:postgresql://localhost/documentstore
      </connection-url>
    <driver>
      postgresql-9.0-801.jdbc4.jar
    </driver>
      <transaction-isolation>
      TRANSACTION_READ_COMMITTED
      </transaction-isolation>
    <pool>
      <min-pool-size>
      3
      </min-pool-size>
      <max-pool-size>
      15
      </max-pool-size>
      <prefill>
      true
      </prefill>
      <use-strict-min>
      false
      </use-strict-min>
      <flush-strategy>
      FailingConnectionOnly
      </flush-strategy>
    </pool>
    <security>
      <user-name>
      documentstore
      </user-name>
      <password>
      xxxxx
      </password>
    </security>
    <validation>
      <check-valid-connection-sql>
      select 1
      </check-valid-connection-sql>
      <validate-on-match>
      false
      </validate-on-match>
      <background-validation>
      false
      </background-validation>
      <useFastFail>
      false
      </useFastFail>
      <exception-sorter
      class-name="org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter"/>
    </validation>
    <timeout>
      <blocking-timeout-millis>
      60000
      </blocking-timeout-millis>
      <idle-timeout-minutes>
      15
      </idle-timeout-minutes>
    </timeout>
    <statement>
      <track-statements>
      TRUE
      </track-statements>
    </statement>
    </datasource>

    1.2.4. Deploy the Jackrabbit Resource Adapter to JBoss

    It should be possible to just copy the resource adapter to the deploy directory but it requires a couple of modifications first before it can be used in JBoss AS7.

    RAR Modifications

    Unpack the rar file (jackrabbit-jca-2.2.7.rar) to make a couple of configuration changes (these should not really be necessary but are for now)

    Dependency on JCR module

    Edit the META-INF/MANIFEST.MF file. Add the Dependencies line (and make sure it has a blank line at the end or it doesn't work). You should end up with a MANIFEST.MF file that looks a bit like this:

    Manifest-Version: 1.0
    Archiver-Version: Plexus Archiver
    Created-By: Apache Maven
    Built-By: bwallis
    Build-Jdk: 1.6.0_26
    Dependencies: javax.jcr export,org.slf4j

    Table 5 MANIFEST.MF

    Most of the entries are not used and can be ignored if different from the above, the manifest version and dependencies lines should be as shown.

    Note that this is also including a dependency on the org.slf4j logging package so we can get the logging from the rar integrated into the jboss logging.

    If you are recreating the rar with the jar utility don't forget the -M flag so that the MANIFEST.MF file is auto-generated (overwriting your changes).

    Remove logging Jars

    When deployed into jboss we want to use the jboss logging framework, not write to standard output so we need to remove the logging implementation included in the rar file to get it to use the jboss logging framework (otherwise logging ends up to stdout). The two jars to remove are  logback-classic-0.9.20.jar and logback-core-0.9.20.jar. They are in the root directory of the rar file.

    Add Repository Paths to ra.xml

    This is temporary to work around a problem in jboss 7 for builds prior to August 5th (see above for jboss bug report link). Find the config-property definitions for HomeDir and ConfigFile and add the values as shown (use appropriate paths to your installation). These values should be defined in standalone.xml/domain.xml but for earlier builds including 7.0.0.Final that doesn't work.

    <config-property>
      <config-property-name>HomeDir</config-property-name>
    <config-property-type>java.lang.String</config-property-type>
      <config-property-value>/Users/bwallis/InfoMedix/JBoss/jboss-as-7.1.0.Alpha1-SNAPSHOT/repository</config-property-value>
    </config-property>
    <config-property>
      <config-property-name>ConfigFile</config-property-name>
      <config-property-type>java.lang.String</config-property-type>
      <config-property-value>/Users/bwallis/InfoMedix/JBoss/jboss-as-7.1.0.Alpha1-SNAPSHOT/repository/repository.xml</config-property-value>
    </config-property>

    1.2.5. Add the Jackrabbit JCA configuration to JBoss

    You need to add the JCA configuration to $(JBOSS_HOME)/standalone/configuration/standalone.xml (or the domain equivalent if running the domain version of jboss)

    Something like this seems to work for a simple test case (see below for test case code)

    <subsystem xmlns="urn:jboss:domain:resource-adapters:1.0">
    <resource-adapters>
    <resource-adapter>
    <archive>
      jackrabbit-jca-2.2.7.rar
    </archive>
    <transaction-support>
    XATransaction
    </transaction-support>
    <connection-definitions>
      <connection-definition class-name="org.apache.jackrabbit.jca.JCAManagedConnectionFactory"
      jndi-name="java:/jca/DocumentStore" enabled="true" use-java-context="true"
      pool-name="jackrabbit-jca-2_2_7_rar-Pool" use-ccm="true">
    <!-- There is a bug in JBoss that prevents the following configuration of the
    properties from working in builds of JBoss AS 7.1.0 prior to August 5th
    so these two property definitions should be left out and configured in the
    ra.xml file in the resource adapter for builds earlier than that (including
    the 7.0.0.Final version). See below and also the bug report at
    https://issues.jboss.org/browse/AS7-1452
    -->
    <config-property name="HomeDir">
    /Users/bwallis/InfoMedix/JBoss/jboss-as-7.1.0.Alpha1-SNAPSHOT/repository
    </config-property>
    <config-property name="ConfigFile">
    /Users/bwallis/InfoMedix/JBoss/jboss-as-7.1.0.Alpha1-SNAPSHOT/repository/repository.xml
    </config-property>
      <pool> 
      <min-pool-size>
      3
      </min-pool-size>
      <max-pool-size>
      15
      </max-pool-size>
      <prefill>
      true   
      </prefill>
      <use-strict-min>
      true   
      </use-strict-min>
      </pool>
      </connection-definition>
    </connection-definitions>
    </resource-adapter>
    </resource-adapters>
    </subsystem>

    2. Configure the Repository

    The path configured in ra.xml or standalone.xml points to a directory for the repository and also to a configuration file for the repository. Two files are required to configure the repository. One is the repository.xml file and the other is a schema (optional).

    The schema is used once to initialise the data model similar in the way that an SQL DDL file is used in a relational database. Once the data model has been loaded you don't need to do it again (ie: not every time you start) unless you are recreating the repository from scratch. The following is an example of a valid schema that has been used during initial evaluation.

    <inf = 'http://infomedix.com.au/inf'>

    [inf:metadata] mixin
    - inf:description (STRING)='' mandatory

    [inf:attribute] > nt:base
    - inf:key (STRING) mandatory
    - inf:value (STRING) mandatory

    [inf:externalReference] > nt:base
    - inf:date (DATE) mandatory
    - inf:endpoint (STRING) mandatory
    - inf:address (STRING) mandatory
    - inf:state (STRING) mandatory

    [inf:content] > inf:metadata, mix:referenceable
    + inf:data (nt:resource) mandatory multiple primary

    [inf:document] > inf:metadata, mix:referenceable
    + inf:pages (inf:content) mandatory multiple primary
    + inf:attributes (inf:attribute) multiple

    [inf:documentGroup] > nt:folder, inf:metadata, mix:referenceable
    + inf:documents (inf:document) mandatory multiple primary
    + inf:sources (inf:externalReference) multiple
    + inf:destinations (inf:externalReference) multiple
    + inf:attributes (inf:attribute) multiple

    Table 6 schema.cnd

    The repository.xml file is used every time the document store is started up. It defines where things are stored (databases, filesystem paths) and configuration for the stores and for the lucene search engine. This example defines a datastore that is persisted in the database using a standard jdbc DataSource.

    When deployed in jboss AS7 the JNDI name of the datasource (the url parameter) is specified as jdbc/DocumentStoreDS but in JBoss AS6 you need to add java: to the front giving java:jdbc/DocumentStoreDS. Note that this is specified in about 6 different places in the repository.xml file and is also specified in any workspace.xml files that JackRabbit may have created after first use (in repository/workspaces/*/workspace.xml).

     

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.5//EN"
      "http://jackrabbit.apache.org/dtd/repository-1.5.dtd">
    <Repository>
    <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
      <param name="driver" value="javax.naming.InitialContext"/>
      <param name="url" value="jdbc/DocumentStoreDS"/>
      <param name="schema" value="postgresql"/>
      <param name="schemaObjectPrefix" value="fsrep_"/>
    </FileSystem>
    <Security appName="Jackrabbit">
    <AccessManager class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager"></AccessManager>
    <LoginModule class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
      <param name="anonymousId" value="anonymous" />
    </LoginModule>
    </Security>
    <DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
    <param name="driver" value="javax.naming.InitialContext"/>
    <param name="url" value="jdbc/DocumentStoreDS"/>
    <param name="databaseType" value="postgresql"/>
      <param name="schemaObjectPrefix" value="ds_" />
    </DataStore>
    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default" />
    <Workspace name="default">
      <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
      <param name="driver" value="javax.naming.InitialContext"/>
      <param name="url" value="jdbc/DocumentStoreDS"/>
      <param name="schema" value="postgresql"/>
    <param name="schemaObjectPrefix" value="fsws_${wsp.name}_"/>
      </FileSystem>
      <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.PostgreSQLPersistenceManager">
    <param name="driver" value="javax.naming.InitialContext"/>
    <param name="url" value="jdbc/DocumentStoreDS"/>
      <param name="schema" value="postgresql" />
      <param name="schemaObjectPrefix" value="pm_${wsp.name}_" />
      <param name="externalBLOBs" value="false" />
      </PersistenceManager>
    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
      <param name="path" value="${wsp.home}/index" />
      <param name="useCompoundFile" value="true" />
      <param name="minMergeDocs" value="100" />
      <param name="volatileIdleTime" value="3" />
    <param name="maxMergeDocs" value="100000" />
      <param name="mergeFactor" value="10" />
      <param name="maxFieldLength" value="10000" />
      <param name="bufferSize" value="10" />
      <param name="cacheSize" value="1000" />
      <param name="forceConsistencyCheck" value="false" />
      <param name="autoRepair" value="true" />
      <param name="analyzer" value="org.apache.lucene.analysis.standard.StandardAnalyzer" />
      <param name="queryClass" value="org.apache.jackrabbit.core.query.QueryImpl" />
      <param name="respectDocumentOrder" value="true" />
      <param name="resultFetchSize" value="2147483647" />
      <param name="extractorPoolSize" value="3" />
      <param name="extractorTimeout" value="100" />
      <param name="extractorBackLogSize" value="100" />
      <param name="textFilterClasses"
      value="org.apache.jackrabbit.extractor.MsWordTextExtractor,
      org.apache.jackrabbit.extractor.MsExcelTextExtractor,
      org.apache.jackrabbit.extractor.MsPowerPointTextExtractor,
      org.apache.jackrabbit.extractor.PdfTextExtractor,
      org.apache.jackrabbit.extractor.OpenOfficeTextExtractor,
      org.apache.jackrabbit.extractor.RTFTextExtractor,
      org.apache.jackrabbit.extractor.HTMLTextExtractor,
      org.apache.jackrabbit.extractor.PlainTextExtractor,
      org.apache.jackrabbit.extractor.XMLTextExtractor" />
      </SearchIndex>
    </Workspace>
    <Versioning rootPath="${rep.home}/version">
      <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
      <param name="driver" value="javax.naming.InitialContext"/>
      <param name="url" value="jdbc/DocumentStoreDS"/>
      <param name="schema" value="postgresql"/>
    <param name="schemaObjectPrefix" value="fsver_"/>
      </FileSystem>
      <PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.PostgreSQLPersistenceManager">
    <param name="driver" value="javax.naming.InitialContext"/>
    <param name="url" value="jdbc/DocumentStoreDS"/>
      <param name="schema" value="postgresql" />
      <param name="schemaObjectPrefix" value="version_" />
      <param name="externalBLOBs" value="false" />
    </PersistenceManager>
    </Versioning>
    </Repository>

    Table 7 repository.xml

    3. Debug Test Code

    To keep the testing simple I just used a JSP in a simple war file

    The war file structure is:

    1. jndi.jsp
      META-INF/
      META-INF/MANIFEST.MF
      test.jsp
      WEB-INF/
      WEB-INF/lib/
      WEB-INF/lib/jackrabbit-jcr-client-2.2.7.jar

    The manifest contains the following

    Manifest-Version: 1.0
    Dependencies: javax.jcr,org.jboss.as.naming,deployment.jackrabbit-jca-2.2.7.rar

    Table 8 MANIFEST.MF

    the lib directory needs the jackrabbit client jar file as we need some of the classes from the test jsp file

    The test.jsp file contains the following little test program. This installs a schema written in CND notation and then inserts and retrieves a couple of nodes.

     

    Note: this code depends on using the CND schema given above. You could do something similar without the schema but that is an exercise left to the reader :-)

     

     

    <%@ page  language="java" import="java.util.*,java.io.*,javax.naming.*,javax.jcr.*,org.apache.jackrabbit.api.*,org.apache.jackrabbit.core.*" errorPage="" %>
    <html>
    <body>
    <%
    Context ctx = new InitialContext();
    Repository repository = (Repository) ctx.lookup("java:/jca/DocumentStore") ;  //For JBoss 6
    //  Repository repository = (Repository) ctx.lookup("jca/DocumentStore") ;  //For JBoss 7

    %>
    <pre>
    Have Repository Reference: <%=repository.toString()%>
    <%
    Session ses = repository.login(new SimpleCredentials("username", "password".toCharArray()));
    %>
    Have logged in
    <%
    try {
      JackrabbitNodeTypeManager manager = (JackrabbitNodeTypeManager)ses.getWorkspace().getNodeTypeManager();
    // Register the custom node types defined in the CND file
    manager.registerNodeTypes(new FileInputStream("/Users/bwallis/InfoMedix/JBoss/jboss-as-7.1.0.Alpha1-SNAPSHOT/repository/schema.cnd"),JackrabbitNodeTypeManager.TEXT_X_JCR_CND);

    %>
    Loaded Node Definitions
    <%
    Node root = ses.getRootNode();

    // Store content
    Node docGroup = root.addNode("documents_1","inf:documentGroup");
    Node attr = docGroup.addNode("inf:attributes","inf:attribute");
      attr.setProperty("inf:key","name");
      attr.setProperty("inf:value","a value");
    Node doc = docGroup.addNode("inf:documents","inf:document");
    Node dpage = doc.addNode("inf:pages","inf:content");
    Node data = dpage.addNode("inf:data","nt:resource");
      data.setProperty("jcr:data","stuff");
      docGroup.setProperty("inf:description", "Hello, World, this is a Document Group");
      doc.setProperty("inf:description", "Hello, World, this is a Document");
      dpage.setProperty("inf:description", "Hello, World, this is a Page");
    %>
    Created nodes, about to persist...
    <%
    ses.save();
    %>
    Persisted nodes. Retrieve one back.
    <%
    // Retrieve content
    Node node = root.getNode("documents_1/inf:documents");
    %>
    node.getPath() = <%=node.getPath()%>
      node.getProperty("message").getString() = <%=node.getProperty("inf:description").getString()%>
    <%
    // Remove content
      root.getNode("documents_1").remove();
    ses.save();
    }
    catch(Throwable e) {
    %>
    Exception: <%=e.toString()%>
    <%
    }
    finally {
    ses.logout();
    }
    %>
    </body>
    </html>

    Table 9 test.jsp

    and on running via the url http://localhost:8080/test/test.jsp you should get output similar to that shown below.

    Have Repository Reference: org.apache.jackrabbit.jca.JCARepositoryHandle@75eee7b7

    Have logged in

    node.getPath() = /hello/world
      node.getProperty("message").getString() = Hello, World!