JCR-1907 and WebSphere 6.1

This is a continuation of the previous article I wrote about working with BRMS 5.0.2 with Oracle 11, and JNDI. Here however we will focus on getting around JCR-1907 specifically for WebSphere. The assumption is still that you are using Apache Jackrabbit 1.5.

To backtrack a little, when using JNDI for datasource lookup, the AS will wrap the underlying java.sql.Connection. When using JBoss it is wrapped in org.jboss.resource.adapter.jdbc.WrappedConnection. When using WebSphere you have the joy of seeing the following exception instead:

javax.jcr.RepositoryException: failed to load repository properties: failed to persist repository properties: null: failed to persist repository properties: null: null
...
Caused by: java.lang.ClassCastException: com.ibm.ws.rsadapter.jdbc.WSJdbcConnection
      at oracle.sql.BLOB.createTemporary(BLOB.java:530)
...

So the wrapper class in WebSphere is com.ibm.ws.rsadapter.jdbc.WSJdbcConnection which is contained in com.ibm.ws.runtime_6.1.0.jar.

To get around this issue one solution is to write a WebSphere-specific fix in the source of org.apache.jackrabbit.core.fs.db.OracleFileSystem, so let’s get started…

1) Create a new Java project in Eclipse (New->Other->Java Project) and let’s call it JCR-1907
2) Create a package called com.ibm.ws.jackrabbit.core.fs.db. Your project so far should look like this:

3) Create this package now create a new class called WSOracleFileSystem:

4) Now we want to paste the source of org.apache.jackrabbit.core.fs.db.OracleFileSystem.java as the base for our new class. Copy the body of OracleFileSystem and the imports to your class. Also upfront let’s do couple of obvious changes:
4.1) On line 35 change

private static Logger log = LoggerFactory.getLogger(OracleFileSystem.class);

to

private static Logger log = LoggerFactory.getLogger(WsOracleFileSystem.class);

4.2) and on line 51 fix the constructor name from:

public OracleFileSystem() {

to

public WsOracleFileSystem() {

If you look at your class in the editor now you will see a lot of errors:

This is because we do not have the project classpath set up to include all needed jars, so let’s do that now before we continue.
5) Right-click on your project and select Properties. Go to “Java Build Path” and select the Libraries tab. Click on “Add External Jars” and lets start adding jars that we need:
5.1) Add all Jackrabbit jars in your $app_server/deploy/jboss-brms.war/WEB-INF/lib direcory
5.2) From the same directory also add the jcr-1.0.jar
5.3) From the same directory also add all the slf4j jars
5.4) From the same directory also add the common-io-1.4.jar
Our class looks somewhat better now as far as errors goes but we are still not there. Let’s focus on unresolved import of DbFileSystem:

OracleFileSystem and DbFileSystem are in the same package so there was no need for an import, but now we need to actually import DbFileSystem so on this line right after “exteds DbFileSystem”
6) Press CTRL-Space and Eclipse will add a new import to your class, namely: import org.apache.jackrabbit.core.fs.db.DbFileSystem;
7) On line 139 now we have:

InputStream in = OracleFileSystem.class.getResourceAsStream(schema + ".ddl");

which also produces an error because we do not have an import for OracleFileSystem. Same thing here press CTRL-Space right after “= OracleFileSystem” and Eclipse will add an import for you namely import org.apache.jackrabbit.core.fs.db.OracleFileSystem;
Our class should be error-free now (except a couple of warnings which are fine), and we are ready to actually apply the fix for which this is all about:
8 ) Go back to the project Java Build Path properties and add com.ibm.ws.runtime_6.1.0.jar to the list of project libraries.
9) On line 42 add a new field to the class called wsCon:

private Connection wsCon;

10) in the init() method now in the try/catch block starting on line 99 change the block to:

// use the Connection object for using the exact same
        // class loader that the Oracle driver was loaded with
        try {
        	if (con instanceof WSJdbcConnection) {
        		wsCon = (Connection)WSJdbcUtil.getNativeConnection((WSJdbcConnection)con);
            }
        	blobClass = wsCon.getClass().getClassLoader().loadClass("oracle.sql.BLOB");
            durationSessionConstant =
                    new Integer(blobClass.getField("DURATION_SESSION").getInt(null));
            modeReadWriteConstant =
                    new Integer(blobClass.getField("MODE_READWRITE").getInt(null));
        } catch (Exception e) {
            String msg = "failed to load/introspect oracle.sql.BLOB";
            log.error(msg, e);
            throw new FileSystemException(msg, e);
        }

As you can see we are checking to see if the connection is of type WSJdbcConnection and then using WSJdbcUtil to convert it to the jdbc Connection. We also want to use wsCon to create the instance of blobClass of type oracle.sql.BLOB. This was the root cause of the ClassCastException in the logs.
11) add the imports for com.ibm.ws.rsadapter.jdbc.WSJdbcConnection and com.ibm.ws.rsadapter.jdbc.WSJdbcUtil with CTRL-Space as described earlier.
12) Last change we have to make is in the createTemporaryBlob method namely on line 527 change

Object blob = createTemporary.invoke(null,
                new Object[]{con, Boolean.FALSE, durationSessionConstant});

to

Object blob = createTemporary.invoke(null,
                new Object[]{wsCon, Boolean.FALSE, durationSessionConstant});

We are ready now to export our WsOracleFileSystem as a jar file so we can use it in BRMS.
13) Right-click on the project and select “Export”. Navigate to “Java” and select “Jar File”. Click next.
14) Make sure that our WsOracleFileSystem class is selected as a class to be exported:

15) Choose an export destination (for example I used /Users/tihomir/Desktop/jcr1907.jar) and click “Finish”. Eclipse will tell you that it exported the jar with some warnings. This is OK.
16) Copy this jar to $app_server/deploy/jboss-brms.war/WEB-INF/lib directory

Almost there 🙂

Now we actually have to use WsOracleFileSystem. For this we need to change our repository.xml configuration file:
17) Basically the difference is that everywhere where you configure name sure that you have:

<FileSystem class="com.ibm.ws.jackrabbit.core.fs.db.WsOracleFileSystem">

Delete your repository and workspaces directories if they are present and I would also recommend starting from fresh, so if the database schema you are using already contains some tables, drop and recreate the entire schema and don’t just drop the tables as Jackrabbit also creates some triggers that wont be deleted with just dropping tables.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: