Thursday, April 23, 2009

Make ColdFusion 8 work with Apache CXF

"Alan" left a comment on one of my older posts ("ColdFusion 8 Getting Started code available") detailing how to get ColdFusion 8 to work with Apache CXF. I figured it would be useful to repost in its own entry.

DISCLAIMER: I have not tried this, nor do I make any guarantees that this wont do bad stuff to your server. Try it on a Developer Edition on your desk before messing with any production server.


If anyone is interested, I have been able to make ColdFusion 8 work with Apache CXF, which supports a variety of web service standards and libraries - SOAP 1.1, SOAP 1.2, REST, etc.

Caveats: CF8 works with CXF in the sense that CXF objects can be instantiated as Java objects in CFML and their operations successfully invoked. It does NOT work as a native CF web service call using or CreateObject("webservice", "..."). Also, this technique requires some low-level changes to the default CF8 installation, so it is not for the faint of heart.

In general, the procedure is to grab the CXF libraries and get CF to recognize them. You can drop them directly into a /lib folder that's in the CF classpath, or if you want to be a bit more cautious you can point the CF classpath to the folder that contains the CXF JAR files.

Detailed steps:
  1. Download the latest CXF distribution and extract/install it somewhere. If your CXF root is /foo, then find /foo/lib. You'll see a bunch of JAR files, and your mission is to get CF to recognize these files and load the classes in them.

  2. Stop the CF server.

  3. CF8 and the JRE that comes with it use an older, incompatible version of the JAXB library, so we need to get the newer one in place. Create /foo/lib/endorsed and copy the jaxb-api-version.jar file into that folder.

  4. Find jvm.config in your CF installation and open it in a text editor. Append the following to the JVM arguments: -Djava.endorsed.dirs=path_to_foo/lib/endorsed -Djava.protocol.handler.pkgs=javax.net.ssl. Find the string -Dcoldfusion.classPath= in the JVM arguments and place the path to /foo/lib immediately after the equals sign (i.e., at the FRONT of the classpath), with a comma to separate it from the rest of the classpath entries. Do not set the classpath using the CF administrator. It will not permit adding elements to the front of the classpath, and will in fact overwrite the classpath if it is used to change Java settings at any future point.

  5. A suspected bug in the CF classloader causes the wrong part of the CF architecture to load the SAAJ classes. [Tom Here: This isn't a bug, CF maintains absolute control over which classes it loads via its own classloader. A better workaround would be to edit the jrun.properties file in the WEB-INF/lib/cfmx_bootstrap.jar file and change the exceptions list] Get around this by copying /foo/lib/saaj-api-version.jar into {java.home}/lib of your CF installation. Then delete or rename saaj-api-version.jar in /foo/lib so it does not get loaded from that location. Also delete or rename saaj.jar from the main CF library location (ColdFusion8\lib on a Windows installation, standalone configuration).

  6. Disable the native JAXB library in ColdFusion by deleting or renaming jaxb-impl.jar in the main CF library location.

  7. Restart the CF server.


With these steps, you will be able to invoke Java objects that serve as clients for the web service endpoints. These Java objects need to be created, compiled, and installed to a location in the CF classpath in order to be invoked. The CXF documentation describes ways to create the Java objects; wsdl2java might be your best bet. You can have it create classes that are SOAP 1.1 and SOAP 1.2 compatible, allowing you to call those services via the Java objects from inside ColdFusion files.

After doing this, I did a cursory test of the native CF web service functionality (Axis 1.1) and it seems to work still, so existing code shouldn't be affected.



There ya go, I would be interested in posting an update if Alan or someone else figures out what the classloading issues with the SAAJ libraries are.
Thanks Alan!

7 comments:

Alan said...

Hi, Alan here.

The reason I believe it's a bug in the CF classloader is that the package name for the SAAJ API is javax.xml.soap. From what I understand, the Java specification requires all ClassLoader implementations to delegate the loading of classes in the java.* and javax.* namespaces to the bootstrap classloader. It appears that the CF classloader is not adhering to the Java spec but is loading the SAAJ classes instead of letting the bootstrap loader do it.

Tom said...

Hi Alan,

As I said, ColdFusion maintains strict control over which classes it loads and from where. This is fully configurable in the cfmx_bootstrap.jar properties file on a per application server basis.

The reason we need to do this is because many application servers provided older (or newer) versions of many 'standard' classes, including javax.* classes, which oftentimes break our functionality. We can't let that happen, so we wrote our own classloader (which many complex web applications do) that ensures we know which classes we are using.

We do in fact load the SAAJ classes from our lib directory because in order for the Axis engine embedded in CF to work every time, we have to use the SAAJ implementation provided by Axis.

daves said...

Hi Tom,

In step 6 you suggest disabling the jaxb-impl. My requirement is to use jaxb2.1 and to this end I have replaced the jaxb-impl with a 2.1 compatible version. However on CF start up I get a class not found error for com.sun.xml.bind.RIElement (depricated in v2). This seems to have no effect and am happily unmarshalling and marshalling xml.
My question is do you know why cf is trying to load RIElement and do you know how to stop it?

Thanks Dave

Tom said...

@Dave No, sorry I don't know what the dependency is. It could be one of the many (many) jar files that CF uses (see the lib directory) or (more likely) it is some sort of CFX dependency.

daves said...

Ok Thanks will keep hunting

Rick Root said...

Tom,

Would this allow me to more easily consume a remote web service that has ws-policy stuff in the WSDL? I know Axis doesn't support ws-policy but CXF does....

Tom said...

@Rick,

Yes, if you get CFX (or Axis 2) to run under CF you could certainly get access to any WS-Policy functionality that CFX has to offer users from its Java APIs.

The Java interop in CF is really good.