jmurty/java-xmlbuilder

Allow to extend XMLBuilder (remove final from class and protected constructors)

Closed this issue · 1 comments

Currently, XMLBuilder always construct DocumentBuilderFactory, TransformerFactory and XPathFactory. I assume as they are factories they can be safely cached. Even if they aren't thread-safe, it should be no problem to wrap in ThreadLocal or make a pool, or whatever.

We are using xerces implementation for parser and it seems like every invocation of DocumentBuilderFactory.newInstance() requires some sort of dynamic class loading. On a heavy load, we are seeing java threads are getting blocked in class loader when they are all are trying to load a document builder factory.

E.g:

"http-apr-8080-exec-2" #3939 daemon prio=5 os_prio=0 tid=0x00007f7d20087800 nid=0x5b90 waiting for monitor entry [0x00007f7ad3f2c000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at java.util.zip.ZipFile.getEntry(ZipFile.java:308)
        - waiting to lock <0x00000005406689c8> (a java.util.jar.JarFile)
        at java.util.jar.JarFile.getEntry(JarFile.java:240)
        at java.util.jar.JarFile.getJarEntry(JarFile.java:223)
        at sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:1005)
        at sun.misc.URLClassPath$JarLoader.findResource(URLClassPath.java:983)
        at sun.misc.URLClassPath.findResource(URLClassPath.java:188)
        at java.net.URLClassLoader$2.run(URLClassLoader.java:569)
        at java.net.URLClassLoader$2.run(URLClassLoader.java:567)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findResource(URLClassLoader.java:566)
        at java.lang.ClassLoader.getResource(ClassLoader.java:1093)
        at java.lang.ClassLoader.getResource(ClassLoader.java:1088)
        at java.net.URLClassLoader.getResourceAsStream(URLClassLoader.java:232)
        at org.apache.catalina.loader.WebappClassLoader.getResourceAsStream(WebappClassLoader.java:1542)
        at org.apache.xerces.parsers.SecuritySupport$6.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.apache.xerces.parsers.SecuritySupport.getResourceAsStream(Unknown Source)
        at org.apache.xerces.parsers.ObjectFactory.findJarServiceProvider(Unknown Source)
        at org.apache.xerces.parsers.ObjectFactory.createObject(Unknown Source)
        at org.apache.xerces.parsers.ObjectFactory.createObject(Unknown Source)
        at org.apache.xerces.parsers.DOMParser.<init>(Unknown Source)
        at org.apache.xerces.parsers.DOMParser.<init>(Unknown Source)
        at org.apache.xerces.jaxp.DocumentBuilderImpl.<init>(Unknown Source)
        at org.apache.xerces.jaxp.DocumentBuilderFactoryImpl.newDocumentBuilder(Unknown Source)
        at com.jamesmurty.utils.BaseXMLBuilder.createDocumentImpl(BaseXMLBuilder.java:134)
        at com.jamesmurty.utils.XMLBuilder.create(XMLBuilder.java:111)
        at com.jamesmurty.utils.XMLBuilder.create(XMLBuilder.java:130)

Please, allow us to extend your XMLBuilder to make it possible to 1) access the constructor, so we it can cope with our own cached document builder factory 2) extend XMLBuilder class to provide other useful methods for our domain.

Hi, unfortunately I am not willing to remove the final status of XmlBuilder and XmlBuilder2 because these classes cannot simply be extended to add or change functionality. To create a new builder implementation that will work properly with method-chaining you must implement all the abstract methods from BaseXmlBuilder specifically for the new class, and the chances of getting this right are slim. Overriding these classes is just a bad idea.

To make it possible to use a cached DocumentBuilderFactory I could refactor the BaseXMLBuilder methods createDocumentImpl and parseDocumentImpl to allow a factory to be passed in, and do the same for the builders' create method.

But for you to be able to extend the builder with this and potentially other customisations as well, I think your best option is to create your own XMLBuilder2 or XMLBuilder implementation by extending the BaseXMLBuilder. You could just copy/paste the code from one of the existing ones, then adapt it freely to change how it calls createDocumentImpl etc.