2

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.

Chris Knight
  • 24,333
  • 24
  • 88
  • 134

5 Answers5

3

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.

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • Thanks. Debuging did verify that there are (at least) two instances of the static jaxbContexts. This code is in an abstract base class with multiple concrete implementations. Is there one static instance of this field per subclass? – Chris Knight Jan 20 '11 at 14:58
  • The method is not static, so it synchronizes in the object instance (not the Class instance, which gives different locks so no locking between them). The map is static, but it's used concurrently without locking, so between the time contains is done and it is added, another thread can already be doing the same for the same key. Using sychronized(jaxContents) {all code here} might work. – Geoffrey De Smet Jan 20 '11 at 15:41
  • "Is there one static instance of this field per subclass?" No. – Geoffrey De Smet Jan 26 '11 at 13:05
1

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);
}
Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
  • Ok, but this doesn't affect the outcome which suggests that containsKey is working and my issue in fact lies with multiple static instances of the jaxbContexts. – Chris Knight Jan 20 '11 at 14:57
  • Yes, I know this doesn't actually address the issue. That's why I voted up @Geoffrey's answer. – Paul Tomblin Jan 20 '11 at 15:05
1

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.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
0

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)

cadrian
  • 7,332
  • 2
  • 33
  • 42
  • Thanks. I originally was using the class itself as the key, but switched to class name while trying to get to the bottom of this issue. – Chris Knight Jan 20 '11 at 14:39
-1

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

卢声远 Shengyuan Lu
  • 31,208
  • 22
  • 85
  • 130