May 22nd, 2011

Java: Deployment issues

Being new to developing Java web applications using a lot of dependencies, I encountered a few issues when deploying an update onto our Geronimo app server. Since some were hard to find solutions for, I decided to write them down in this blog post. I didn’t have any of these issues while running the application for development by mvn jetty:run (even on the same machine as Geronimo).

  1. Doubled dependencies
    We use Quartz Scheduler in our application while Geronimo itself also uses Quartz (however, in an older version). That resulted in a module conflict indicated by an IncompatibleClassChangeError since both libraries can not be loaded at the same time within the same classloader. The solution to this was rather simple: all that was necessary is adding <inverse-classloading /> to the deployment plan (which may better be placed in a separate directory so you can easily switch between multiple deployment targets). If our application was using Quartz 1.6 (the version Geronimo is using) I might have simply added a dependency to my deployment plan and could have shared the classes.
  2. Missing XML implementation
    Our application also writes PDFs using the Apache XSL-FO processor. For a reason I still don’t understand, there appeared to be only stubs available but no implementations although it worked happily with apparently the same configuration on the same machine when run from mvn jetty:run instead of Geronimo. The message I got was something like “org.apache.xerces.jaxp.SAXParserFactoryImpl not found”. After a lot of search and failed tries I figured out that I simply had to add xerces/xercesImpl as a dependency. Another option may have been to dig deeper into property handling and figure out how to properly solve the problem by specifying an existing implementation as suggested on an older StackOverflow question. (however I’m unsure if that really was the problem as it appeared that the classes were missing but the class name was correct and it worked fine from command line so the classes – to my understanding – should have been available through the default class loader)
  3. LinkageError
    The last problem I had to deal with took me much longer to figure out. Take a look at this part of a stack trace:

    
    Caused by: java.lang.LinkageError: loader constraint violation: when resolving method "javax.imageio.metadata.IIOMetadata.getAsTree(Ljava/lang/String;)Lorg/w3c/dom/Node;" the class loader (instance of org/apache/geronimo/kernel/config/MultiParentClassLoader) of the current class, org/apache/xmlgraphics/image/loader/impl/imageio/ImageIOUtil, and the class loader (instance of <bootloader>) for resolved class, javax/imageio/metadata/IIOMetadata, have different Class objects for the type org/w3c/dom/Node used in the signature
    	at org.apache.xmlgraphics.image.loader.impl.imageio.ImageIOUtil.extractResolution(ImageIOUtil.java:54)
    	at org.apache.xmlgraphics.image.loader.impl.imageio.PreloaderImageIO.preloadImage(PreloaderImageIO.java:101)
    	at org.apache.xmlgraphics.image.loader.ImageManager.preloadImage(ImageManager.java:175)
    	at org.apache.xmlgraphics.image.loader.cache.ImageCache.needImageInfo(ImageCache.java:128)
    	at org.apache.xmlgraphics.image.loader.ImageManager.getImageInfo(ImageManager.java:122)
    	at org.apache.fop.fo.flow.ExternalGraphic.bind(ExternalGraphic.java:81)
    	at org.apache.fop.fo.FObj.processNode(FObj.java:123)
    	at org.apache.fop.fo.FOTreeBuilder$MainFOHandler.startElement(FOTreeBuilder.java:282)
    	at org.apache.fop.fo.FOTreeBuilder.startElement(FOTreeBuilder.java:171)
    	at org.apache.xalan.transformer.TransformerIdentityImpl.startElement(TransformerIdentityImpl.java:1020)
    	at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
    	at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
    	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
    	at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    	at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    	at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    	at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
    	at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:432)
    

    Let’s take a step back from that clutter of information, take a deep breath and examine the top-most sentence a bit closer:

    • we have some sort of class loader conflict
    • the method getAsTree in class javax.imageio.metadata.IIOMetadata refers to org.w3c.dom.Node
    • the class loader we are coming from is “MultiParentClassLoader”, provided by Geronimo
    • we come from org.apache.xmlgraphics.image.loader.impl.imageio.ImageIOUtil
    • the class loader being used by the method we try to call is called “bootloader”
    • the access to “bootloader” originates from javax.imageio.metadata.IIOMetadata
    • both class loaders link to incompatible and thus conflicting signatures of org.w3c.dom.Node, so we cannot continue

    Let’s interpret these facts: org.apache.xmlgraphics..ImageIOUtil calls a method from javax.imageio.metadata.IIOMetadata. We have at least two class loaders that have different understandings of what org.w3c.dom.Node should look like and both classes we access are using a different one of these signatures. Apparently the javax packages are provided by the JDK and are being used by xmlgraphics, so our dependency of xmlgraphics appears to be incompatible with the JDK we are running. We are confident that both the JDK and xmlgraphics are up-to-date, so what next?

    I searched for over an hour and couldn’t find anything relevant except some voodoo stuff. At first I tried to hide classes by adding a filter in the deployment plan; then I tried to enforce one specific version by adding a direct dependency to org.w3c.dom. It was a bug report saying something like “strange, org.w3c.dom hasn’t been touched for years” plus another report saying “use xml-apis-1.3.04″ that got me on the right track: Today’s Java versions seem to ship with at least some org.w3c.dom classes. However, Maven pulled xml-apis as a dependency nevertheless which includes its own versions of org.w3c packages but doesn’t seem to be a problem unless deployed to the app server. Maybe that’s a side-effect of the inverse classloading we enabled to get Quartz running. The solution was to simply exclude that dependency in the pom file. If you are using NetBeans you can simply right-click xml-apis and select “Exclude Dependency” which will automatically add

    
                <exclusions>
                    <exclusion>
                        <artifactId>xml-apis</artifactId>
                        <groupId>xml-apis</groupId>
                    </exclusion>
                </exclusions>
    

    to all relevant dependency definitions (in my case xerces/xercesImpl and org.apache.xmlgraphics/fop).

One Response to “Java: Deployment issues”

  1. Sébastien says:

    Im sorry, xml tags did not appear in comment :/

    With Geronimo, don’t use inverse-classloading ! Never !

    Use the filter feature instead. This will “hide” Quartz to the App classloader. I personnally use this config :

    (sys:hidden-classes)
    (sys:filter)org.apache.commons.logging(/sys:filter)
    (sys:filter)org.quartz(/sys:filter)
    (/sys:hidden-classes)