Thursday, November 19, 2009

Using Jackrabbit with Oracle, JNDI, and Tomcat

This took me weeks to figure out completely, but I think this is the simplest possible solution unless you want to just put the connection info in your jackrabbit xml config directly.  I'm using Jackrabbit 1.5 instead of 1.6 because that's the version in Liferay 5.2.3 and I didn't want to upgrade besides getting the latest point release.  I tried the patch at http://wiki.apache.org/jackrabbit/JNDI but had problems since it's for an older Jackrabbit and I wasn't interested in debugging it.  I tried the instructions at http://wiki.apache.org/jackrabbit/UsingJNDIDataSource but they weren't sufficient since Oracle threw a ClassCastException when jackrabbit tried to create blobs using the DBCP pooled database connection classes.  I tried working around those exceptions with overly complicated class reflection code in the OracleFileSystem but that was a waste of time.  Then I looked at the Oracle JDBC driver javadoc and noticed that they have their own DataSource pooling classes that return Oracle Connection objects which should work with Oracle's blob code.

I did this with version 10.2.0.4 of the Oracle JDBC driver (ojdbc14.jar).
  1. Patch Jackrabbit. 
    1. Download the source, open jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ConnectionFactory.java
      Change

      if (user == null && password == null) {
      To

      if ((user == null && password == null) || (user.length() == 0 && password.length() == 0)) {

      The goal is to rely on the JNDI Resource to define the username and password instead of expecting them in jackrabbit's repository.xml.  This breaks the (useless) case where you actually want to use both a blank username and password for your database connection.  So don't do that.  Otherwise it should still work with both non-JNDI style and JNDI datasources.
    2. Run "mvn install" or "mvn package" in the root jackrabbit source directory.  For me, it didn't matter which command I used since Liferay doesn't get built with maven so I had to manually copy the modified jackrabbit jars to the lib/portal folder regardless.  If you're using maven on your project, do "mvn install" and skip the next step
    3. If you're not using maven, find the new jackrabbit jars you built and copy them to your project (jackrabbit-core-1.5.7.jar / jackrabbit-core.jar is the only one we changed, but I grabbed all the ones already in Liferay for good measure)
  2. Define your DataSource in the tomcat config (I mostly stole this part from Gregg Lagnese, of MicroDeveloper, Inc ).  The key here is to use Oracle's DataSource and Factory classes to avoid the ClassCastException I mentioned in the introduction paragraph above.
    1. Either in tomcat's server.xml or your application's context.xml add a resource

      <Resource name="jdbc/JackrabbitPool"
        auth="Container"
        type="oracle.jdbc.pool.OracleDataSource"
        driverClassName="oracle.jdbc.OracleDriver"
        factory="oracle.jdbc.pool.OracleDataSourceFactory"
        url="jdbc:oracle:thin:<schema>/<password>@<server>:<port>:<instance>"  />
      Attempts to put the username and password as separate attributes from the url (aka the standard way of doing it) failed.  It has to be in the url to work.
    2. Liferay specific note: I had to specify separate resources for Jackrabbit and Liferay since Liferay had problems with the Oracle DataSource class.  That wasn't a big deal to me since I'd rather have my database connection info appear twice in context.xml instead of appearing once in context.xml, again five or six times in repository-ext.xml, and then in repository.xml and workspace.xml once Liferay is deployed.
  3. Edit repository.xml (Liferay people edit ext-impl/src/com/liferay/portal/jcr/jackrabbit/dependencies/repository-ext.xml)
    1. Set the driver classes to javax.naming.InitialContext
    2. Set the urls to "java:comp/env/jdbc/JackrabbitPool"
    3. Use the Oracle specific classes:
      org.apache.jackrabbit.core.fs.db.OracleFileSystem
      org.apache.jackrabbit.core.persistence.db.OraclePersistenceManager
    4. Save/deploy.  If you're using Liferay don't forget to delete tomcat\data\jackrabbit so your old version of repository.xml doesn't linger and make you think it's working when it isn't.
That's it.  Let me know if you tried it and how it went.  This should also work for JBoss application server and probably all of the other J2EE servers that either fork or use Tomcat.

4 comments:

Rodrigo Márquez said...

Hello, I'm facing this problem too but the source of jackrabbit 1.5 are no longer available in the jackrabbit website. Please, can you send me the libs patched?

I have the same problem but with jboss I suppose that the context.xml is my xxx-DS.xml file.


Thank you very much.

he said said...

From the downloads page, find the Release Archive section and you'll be able to click through to http://archive.apache.org/dist/jackrabbit/1.5.7/

Priit said...

urls had to be set to jdbc/MyDataSource for my Liferay 5.2.3

he said said...

My mistake. I updated the last step to be consistent w/ the previous ones.