Question

I'm thoroughly confused by this one. I'm running a full package worth of unit tests. Here's the relevant shared code which gets used by a number of JUnit tests:

private static Map<String, JAXBContext> jaxbContexts = 
                             new HashMap<String, JAXBContext>();

private synchronized JAXBContext getJAXBContext(Class clazz) throws JAXBException {
    JAXBContext context = null;
    if (jaxbContexts.containsKey(clazz.getName())) {
        context = jaxbContexts.get(clazz.getName());
    } else {
        context = JAXBContext.newInstance(clazz);
        System.out.println("Created new context for '" + clazz.getName() + "'");
        jaxbContexts.put(clazz.getName(), context);
    }
    return context;
}

The console output from the JUnit run includes the following two consecutive entries:

Created new context for 'com.somecompany.xmlschema.providepensionpaymentinfo.Interface'
Created new context for 'com.somecompany.xmlschema.providepensionpaymentinfo.Interface'

What am I missing? Why did jaxbContexts.containsKey() not work in this instance for a String based key, unlike 46 other times during the JUnit execution? We aren't running our tests in parallel, but we do use Aspects if that makes a difference.

Was it helpful?

Solution

Debug it and verify that the class that contains this getJAXBContext() method is instantiated only once (by checking it's has the same memory id in debug mode for every call to it). If it's different instantiations, the synchronized keyword will lock on different locks and they will use different maps.

OTHER TIPS

Personally, I wouldn't bother with containsKey.

String name = clazz.getName();
context = jaxbContexts.get(name);
if (context == null) {
    context = JAXBContext.newInstance(clazz);
    System.out.println("Created new context for '" + name + "'");
    jaxbContexts.put(name, context);
}

There's surely nothing special about Map containing strings as keys. Just replace the println by new Exception().printStackTrace() and you'll see what's going on. You may be creating two instances of the class holding the map, or whatever.

Except for a race... But you say that you don't run things in parallel...

Anyway, I would have called context = jaxbContexts.get(clazz.getName()) and tested context against null.

Ah, and used the class itself as a key, because more than one class may have the same name (think class loaders)

The map can be Map<Class, JAXBContext> instead of Map<String, JAXBContext> for easier usage.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top