Thursday, May 19, 2005

Using JAXP 1.3 with JBoss 4

Some of my ADF classes heavily rely on DOM L3 and they won't run with the default settings of JBoss 4.0.2 (and its old endorsed parser). They run just fine with the JAXP 1.3 parser that comes with Java 5.0 so I tried to get rid of the endorsed directory. Unfortunately just removing the directory won't help, as the JAXP 1.3 classes are not added to the classpath by default. I tried to add %JAVA_HOME%\jre\lib\rt.jar to the classpath but this still didn't fix the problem. I sill got a lot errors and the most interesting one was a java.lang.NoClassDefFoundError: org/apache/xerces/xni/parser/XMLEntityResolver.

When I had a look at rt.jar the path to the
XMLEntityResolver class is now com/sun/org/apache/xerces/internal/xni/parser. Trying to hack past the NoClassDefFoundError I also added the contents of the endorsed directory to the classpath (after removing the -Djava.endorsed.dirs=%JBOSS_ENDORSED_DIRS%" and adding rt.jar to the classpath)

Well, this made Jboss start without errors but it has not solved my problem. I was still getting the same error as before when I tried to perform some DOM L3 functions:

Unexpected Error in method: public abstract void net.sf.adf.transport
.JMSTransportRemote.send(net.sf.adf.acl.ACLMessage) throws net
.sf.adf.transport.SendingException,java.rmi.RemoteException

java.lang.AbstractMethodError: org.apache.xml.serialize
.DOMSerializerImpl.getDomConfig()Lorg/w3c/dom/DOMConfiguration;


at net.sf.adf.acl.xml.XMLCodec.encode(XMLCodec.java:63)
at net.sf.adf.transport.JMSTransport.send(JMSTransport.java:133)
at net.sf.adf.transport.JMSTransport.send(JMSTransport.java:119)

The code that caused this exception is this:

Code
public String encode(ACLMessage message) throws EncodingException {
Document doc = encodeDOM(message);
try {
DOMImplementationLS domImplLS =
(DOMImplementationLS)doc.getImplementation();

StringWriter sw = new StringWriter();
LSOutput output = domImplLS.createLSOutput();
output.setCharacterStream(sw);

LSSerializer serializer = domImplLS.createLSSerializer();
DOMConfiguration config = serializer.getDomConfig();

In the end I managed to make it work by adding one final touch to the solution described above. Instead of the (arguably) more portable way of obtaining a DOMImplementation:

Code:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
DOMImplementation domImpl = builder.getDOMImplementation();


I directly used the DOMImplementation from JDK1.5, that is:

Code:
DOMImplementation domImpl = (DOMImplementation)com.sun.org.apache.xerces
.internal.dom.DOMImplementationImpl.getDOMImplementation();

This is not a nice solution, but it was the best I could find on my own. If you have a more elegant I would be interested in finding out about it.

In both cases I had to make sure that the DOMImplementation supports LS 3.0:

Code:
  if (!domImpl.hasFeature("LS", "3.0")) {
throw new ParserConfigurationException(
"Load/Save 3.0 not supported");
}


and finally I used it to create new documents:

Code
Document doc = domImpl.createDocument(namespaceURI,
rootElementQualifiedName, docType);

cast it to a DOMImplementationLS and parse existing ones:

Code:
     DOMImplementationLS domImplLS = (DOMImplementationLS)domImpl;

LSInput input = domImplLS.createLSInput();
input.setCharacterStream(reader);
LSParser parser = domImplLS.createLSParser(
DOMImplementationLS.MODE_SYNCHRONOUS,
validating ? "http://www.w3.org/TR/REC-xml" : null);
// for schema validation this would be set to
// "http://www.w3.org/2001/XMLSchema"
parser.getDomConfig().setParameter("validate", validating);
Document doc = parser.parse(input);

or serialize in-memory DOM trees:

Code:
     DOMImplementationLS domImplLS =
(DOMImplementationLS)doc.getImplementation();

LSOutput output = domImplLS.createLSOutput();
output.setCharacterStream(writer);

LSSerializer serializer = domImplLS.createLSSerializer();
serializer.write(doc, output);

There are so many people who don't have the slightest idea all these are possible using the DOM and it's very good JDK1.5 implementation. It is a pity that JBoss is still shipping with a hard-wired full-of-bugs old implementation of Xerces when JAXP 1.3 works great.

Essentially it would not be very hard to make JBoss 4 work with JAXP 1.3 instead of 1.2. I just did a quick search in the JBoss 4.0.2 source for the phrase org.apache.xerces and there are only 11 java source files containing it. According to the JAXP 1.3 relese notes the most important incompatibe change in JAXP 1.3 is that the packages for Xerces and Xalan got renamed. This was made on purpose and makes it possible to deliver to the JDK1.4 without using the endorsed standards mechanism. This change lets you reference newer Apache libraries in the classpath, so application developers can use them in the same way that would use any other additions to the Java platform.

I am not saying that moving the implementation of JBoss to JAXP 1.3 will be painless, but this is a necessary evolution. Until that happens I hope that the workarounds I describe above will help you unleash the power of JBoss and DOM L3 processing.

1 Comments:

Anonymous Anonymous said...

Spanish Property for Sale
Almeria Villas Spain and Almeria property
Confused with all the publicity and advertising surrounding Almeria Property? Prefer some refreshing honesty? .......then welcome to Almeria Villas Spain , a family run company where our 20 years experience of living and working in Spain combine to bring you a hand picked selection of Almeria property for sale including villas, townhouses and apartments at prices that other companies find difficult to beat.

11:35 AM  

Post a Comment

<< Home