[jdom-interest] suggested JDOM2 improvements

Rolf Lear jdom at tuis.net
Fri Jan 20 12:28:04 PST 2012


XPath and JDOM have always been very loosely coupled. For years (and still
now) there is no need to have direct support for XPath in JDOM. Saxon does
OK without using any of the JDOM/XPath code for example.

What the XPath code in JDOM does (or should do) is to provide a convenient
interface for the functionality. The native javax.xml.xpath.* entry point
is not useful because the JDOM classes do not conform to the same NODESET
type model.

So, given the alternatives:
1. shoehorn XPath support on top of the javax.xml.xpath.* model
2. continue with JDOM1 XPath model
3. build  new 'better' model
4. remove xpath support form JDOM and let it be a 3rd-party add-on.

I think 3 is the best.

But, there are problems with having the support: if you claim support, it
has to actually work when needed. This in turn means that you have to have
some sort of starting point. Jaxen is the only viable alternative (at the
moment) that I know of simply because it's licensing is permissive enough
and it has the right history with JDOM (sorry Michael, Saxon does not make
sense for a ship-it-with-JDOM library).

So, to make a working default system, but then provide the mechanisms to
customize it.

But, you should not change the 'global' default implementaiton from within
Java code. This is because JDOM is often used in multiple places of code:
for example, eclipse has JDOM built in. Hypothetically lets say the Eclispe
'Git' plugin changes the 'default' XPath backend to some new XPath2.0
custom value, then suddenly the 'CVS' plugin is no longer getting the
results it wants.

Setting a JVM-wide System property is a compromise already. It has real
problems because people think they can race the static initializer to
change the System property before it is used the first time.... It is
accessed only once on the first time the XPathFactory is created.

On the other hand, because XPathFactory instances are specified to be
thread-safe, there is nothing stopping you from doing:

public static final XPathFactory XPATH =
XPathFactory.newInstance("com.example.xpath20.XPathFactory");

Then in your code you can freely use:

XPathCompiled<Object> xp = XPATH.compile("//*");


You have in fact been exploiting one of the major flaws in the JDOM 1.x
XPath library: that there is no way to have multiple concurrent XPath
libraries active at the same time. When you do:

    XPath.setXPathClass(JaxenXPath.class);

you are changing the global JDOM XPath library for all JDOM users in the
same JVM. This is not an OK thing to do from a JDOM API perspective.

Bottom line is that there is no good way to allow the 'world' to change
the default XPathFactory from inside a running JVM.

Allowing the world to create a custom instance is a good compromoise, and
allowing the global default instance to be changed from the command-line is
also a decent compromise.

The best practice would be for you to get your own instance of your own
factory, then use that instance from wherever you need it.


So, if you can think of a better way to allow all JDOM users (in any
potential JVM use-case) to get the JDOMFactory of their choice.

Based on my limited understanding of your environment, it would seem to me
that having a single method on your JDOMUtil class like:

private static final AtomicReference<XPathFactory> myfactory = new
AtomicReference<XPathFactory>();
public static final XPathFactory instance() {
    final XPathFactory ret = myfactory.get();
    if (ret == null) {
      ret = XPathFactory.newInstance("my.custom.factory.ClassName");
      if (myfactory.compareAndSet(null, ret) {
        return ret;
      }
      return myfactory.get();
    }
    return ret;
}

That way you can a single location to access your particular factory. You
never have to worry about the System properties. You can change the factory
at your leaisure, and 'everything just works'.

If your use case is more complicated than that, there is nothing stopping
you from having complete control of your factory simply by not using the
newInstance(String) method at all. There is nothing stopping you from
doing:

public static final XPathFactory myfactory = new
MyFactoryImplementation();


Oh, it is hard to keep things straight in my head between what code I have
on my laptop, and what's in the alpha release, so I'll just talk from the
perspective of what's on my laptop now, and what will be in the next Alpha
release.



Rolf


On Fri, 20 Jan 2012 11:34:14 -0800, Leigh L Klotz Jr
<leigh.klotz at xerox.com> wrote:
> On 01/20/2012 05:56 AM, Rolf Lear wrote:
>> 2. new JDOM2 XPathFactory concept which can have different 
>> implementationback-ends (Jaxen, Saxon, whatever).
> +1
>>
>> 3. XPathFactories are thread-safe and reusable in any threads.
>>
> +1
>>
>> 4. have a single 'default' XPathFactory instance obtainable with
>> XPathFactory.instance(). The default back-end instance() can be changed
>> with a system property.
>>
> This is causing me trouble at the moment.  I have to override the 
> XPathFactory, to provide common function definitions and to avoid 
> performance problems that Java classlibrary and JAXP cause.  In JDOM1 I 
> do this in a static class:
> public class JDOMUtil {
>    static {
>      try {
>        XPath.setXPathClass(JaxenXPath.class);
>      } catch (JDOMException e) {
>        throw new RuntimeException(e);
>      }
> }
> 
> I can be assured that it works, and though I'm not sure under what 
> conditions it throws a checked exception, if it does throw one, it's a 
> system startup failure to be debugged by a system engineer.
> 
> With JDOM 2 alpha I have to do this
> 
> // replaced with
> -Dorg.jdom2.xpath.XPathFactory=com.example.jaxen.JaxenXPath
>    static {
>      if 
>
(!(JaxenXPath.class.getName().equals(System.getProperty(JDOMConstants.JDOM2_PROPERTY_XPATH_FACTORY))))
> 
> {
>        throw new RuntimeException(String.format("JDOM Not set up 
> property with -D%=%", JDOMConstants.JDOM2_PROPERTY_XPATH_FACTORY,
>                                                 
> JaxenXPath.class.getName()));
>      }
>    }
> 
> Now I've got JDOM2 dependencies off in a faraway place of Java CLI, 
> where they can easily get lost.
>> 6. Other back-ends can be used at will by calling the
>> XPathFactory.newInstance(String) method (or some direct constructor on 
>> the
>> Factory if it exposes one). 
> This doesn't help me fix the above problem, because all of the 
> ThreadLocal cache logic and pretty entrypoints into the XPath class 
> itself are hardwired to use the System-property defined constructor.  So

> they might as well not be there.
> 
>> 5. the default 'default' back-end will continue to be Jaxen
>>
> 
> Personally, I'd prefer it if you broke this requirement up into a few 
> parts and made it easy to have a Jaxen backend.
> For example, you might say that there's no XPath support without also 
> loading jdom2.jar and jdom2-jaxen.jar.
> Right now, with Jaxen having JDOM1 support built in, and then JDOM2 
> having Jaxen support built in, it causes a bit of circular confusion 
> trying to get things to work.
> 
> If we could configure JDOM to use Saxon and have it get good performance

> without unnecessary recalculations, we'd not even load Jaxen all.
> 
> Leigh.


More information about the jdom-interest mailing list