June 8, 2012

Jackrabbit with PostgreSQL on JBoss AS 7.1

Posted in Jackrabbit, Java, JCR at 10:56 by theBlackDragon

Getting Jackrabbit (2.4.1 and/or 2.6-SVN) to work on JBoss AS 7.1  has been a long and arduous journey. Most of the information is out there, but it’s fragmented at best so I’ll describe the steps I had  to take to get Jackrabbit to work with JBoss using a PostgreSQL database.

1 Setting up the database

1.2 Registering the JDBC driver

The first order of business is to register the PostgreSQL driver, recent drivers don’t require any entries in the modules directory anymore, just specifying putting the driver jar in the deployments folder and putting the filename in the <driver> element in the JBoss configuration file (standalone/configuration/standalone.xml) suffices (see next section about setting up the datasource).

Alternatively you can create a new folder structure in the modules directory “org/postgres/main” and put the driver jar in there. Then you need to create a module.xml file with the following contents:

  <?xml version="1.0" encoding="UTF-8"?>
  <module xmlns="urn:jboss:module:1.0" name="org.postgresql">
    <resources>
      <resource-root path="postgresql-9.1-901-1.jdbc4.jar"/>
    </resources>
    <dependencies>
      <module name="javax.api"/>
      <module name="javax.transaction.api"/>
    </dependencies>
  </module>

We also need to add a <driver> element to standalone.xml under drivers in the datasource section (just search the file for “drivers”, there’s only one such element in the default configuration) like this:

  <driver name="postgres-jdbc4" module="org.postgresql">
      <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
  </driver>

Here it is important that the module name is the same as the name in module.xml, the driver name can be anything.

This second method makes it easier to update the driver if it is used by multipe projects.

1.2 Setting up the datasource

Next we need to set up a new datasource under the datasource subsystem element:

  <subsystem xmlns="urn:jboss:domain:datasources:1.0">
    <datasources>
      <datasource jta="true" jndi-name="java:/jdbc/DocumentStoreDS" 
                  pool-name="DocumentStoreDS" enabled="true" 
                  use-java-context="true" use-ccm="true">
        <connection-url>jdbc:postgresql://localhost/documentstore</connection-url>
        <driver-class>org.postgresql.Driver</driver-class>
        <driver>postgresql-9.1-901-1.jdbc4.jar</driver>
        <pool>
          <min-pool-size>1</min-pool-size>
          <max-pool-size>4</max-pool-size>
          <prefill>false</prefill>
        </pool>
        <security>
          <user-name>documentstore</user-name>
          <password>password</password>
        </security>
        <validation>
          <check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
        </validation>
      </datasource>
    </datasources>
  </subsystem>

If using the module way of registering the JDBC driver change the <driver> element in the above snipped to refer to the chosen driver name (“postgres-jdbc4” in the example)

If you start JBoss now it should say the driver is available under the chosen JNDI name.

  11:33:55,231 INFO  [org.jboss.as.connector.subsystems.datasources] 
    (MSC service thread 1-1) JBAS010400: Bound data source [java:/jdbc/DocumentStoreDS]

2 Registering the JCR API

Next we have to register the JCR API jar as module. The steps are basically the same as for the PostgreSQL driver (if you chose to go that route).

Create a subdirectory javax/jcr/main under modules, put the jcr-2.0.jar in it and create a module.xml file like this:

  <?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"/>
    </resources>
  </module>

3 Setting up Jackrabbit

3.1 Working around a Jackrabbit issue

JCA requires certain classes to implement the equals and hascode methods, unfortunately current Jackrabbit versions don’t do so (see this JIRA issue)

The easiest solution is to turn off failure upon JCA validation errors in the JBoss configuration (or just turn off validation entirely):

  <subsystem xmlns="urn:jboss:domain:jca:1.1">
      <archive-validation enabled="true" fail-on-error="false"
      fail-on-warn="false"/>
      ...
  </subsystem>

3.2 Specifying Jackrabbit dependencies

Next we need to be able to tell JBoss that Jackrabbit depends on the JCR API. For this we need to unpack the jackrabbit-jca-<version>.rar and change it’s MANIFEST.MF by adding the following line:

  Dependencies: javax.jcr

Then repackage it.

Note that any projects you want to deploy to JBoss that require Jackrabbit *must* also specify this dependency in their manifest.

3.3 Configure Jackrabbit

Now we’re ready to add Jackrabbit to the JBoss configuration as a resource adapter:

  <subsystem xmlns="urn:jboss:domain:resource-adapters:1.0">
    <resource-adapters>
      <resource-adapter>
        <archive>
          jackrabbit-jca-2.4.1.rar
        </archive>
        <transaction-support>XATransaction</transaction-support>
        <connection-definitions>
          <connection-definition 
              class-name="org.apache.jackrabbit.jca.JCAManagedConnectionFactory" 
              jndi-name="java:/jackrabbit" enabled="true" 
              use-java-context="true" 
              pool-name="jackrabbit-jca-2_4_1_rar-Pool" use-ccm="true">
            <config-property name="HomeDir">
              ${jboss.server.data.dir}/jcr-repository
            </config-property>
            <config-property name="ConfigFile">
              ${jboss.server.data.dir}/jcr-repository/repository.xml
            </config-property>
            <xa-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>
            </xa-pool>
          </connection-definition>
        </connection-definitions>
      </resource-adapter>
    </resource-adapters>
  </subsystem>

If you update the repository configuration appropriately you should now be able to start JBoss AS and access Jackrabbit over JNDI.

5 Comments »

  1. […] Jackrabbit with PostgreSQL on JBoss AS 7.1 : 작업의 큰 줄기를 잡는데 큰 도움이 된 글. Jackrabbit 부분은 PostgreSQL + JBoss AS와 관련 없긴 합니다. […]

  2. Sergio said,

    Hi, I have followed these instructions. The repository initializes but looking at traces y see that:

    10:03:38,143 ERROR [stderr] (JCA PoolFiller) Exception in thread “JCA PoolFiller” java.lang.UnsupportedOperationException: Retrieving meta data not supported.

    10:03:38,144 INFO [org.jboss.as.connector.deployers.RaXmlDeployer] (MSC service thread 1-5) IJ020002: Deployed: file:/C:/Java/jboss-as-7.1.1.Final/standalone/tmp/vfs/temp47ed037648017110/jackrabbit-jca-2.6.1.rar-e8994cb1dac1d85f/contents/
    10:03:38,148 ERROR [stderr] (JCA PoolFiller) at org.apache.jackrabbit.jca.AnonymousConnection.getMetaData(AnonymousConnection.java:125)

    10:03:38,152 ERROR [stderr] (JCA PoolFiller) at org.jboss.jca.core.connectionmanager.tx.TxConnectionManagerImpl.createConnectionListener(TxConnectionManagerImpl.java:540)

    Did you have the same error? Thank you!!

    • Yes we see a very similar exception. I haven’t as of yet had the time to look into the cause but it hasn’t caused any trouble for us so far.

      • Sergio said,

        Ok Thanks. Just a note… I had to configure the datasource as not jta. For what I’ve read, jackrabbit handles XA for itself and needs full control about db connections.

  3. civ said,

    I’ve followed this guide but I have this error:
    HTTP Status 500 –

    type Exception report

    message

    description The server encountered an internal error () that prevented it from fulfilling this request.

    exception

    javax.servlet.ServletException: javax.naming.NameNotFoundException: jackrabbit — service jboss.naming.context.java.jackrabbit
    org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:862)
    org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:791)
    org.apache.jsp.test_jsp._jspService(test_jsp.java:156)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:369)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:326)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:253)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:847)

    root cause

    javax.naming.NameNotFoundException: jackrabbit — service jboss.naming.context.java.jackrabbit
    org.jboss.as.naming.ServiceBasedNamingStore.lookup(ServiceBasedNamingStore.java:97)
    org.jboss.as.naming.NamingContext.lookup(NamingContext.java:178)
    org.jboss.as.naming.InitialContext.lookup(InitialContext.java:113)
    org.jboss.as.naming.NamingContext.lookup(NamingContext.java:214)
    javax.naming.InitialContext.lookup(InitialContext.java:411)
    org.apache.jsp.test_jsp._jspService(test_jsp.java:67)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:369)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:326)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:253)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:847)

    note The full stack trace of the root cause is available in the JBoss Web/7.0.13.Final logs.


Leave a comment