Monday, October 21, 2013

EclipseLink bug with @MappedSuperclass

Today I fought through an issue with EclipseLink / JPA. We are upgrading from TopLink (*really, really old*) to EclipseLink, but the version of EclipseLink we were using was 2.0.2, based on another project in our stack.

We use static weaving in our JPA projects. For some reason, any class marked with @MappedSuperclass wouldn't get weaved correctly, leading to an error similar to this:

Exception Description: The method [setXXXXXXX] on the object is throwing an exception. Argument: [1.18] Internal Exception: java.lang.reflect.InvocationTargetException Target Invocation Exception: java.lang.NoSuchMethodError: com.blah.MyClass._persistence_checkFetched(Ljava/lang/String;)V Mapping: org.eclipse.persistence.mappings.DirectToFieldMapping[myFieldInClass-->MYTABLE.MYFIELDINCLASS] Descriptor: RelationalDescriptor(com.blah.MyClass --> [DatabaseTable(MYTABLE.MYFIELDINCLASS)])] with root cause java.lang.NoSuchMethodError: com.blah.MyClass._persistence_checkFetched(Ljava/lang/String;)V

The method it was trying to find was the "getter" for that field - which did, in fact, exist! I searched through a ton of forums with no help, so I finally decided to get the latest EclipseLink jar version (2.5.1 as of this post) and latest JPA spec jar (2.1.0). Voila...everything works perfectly. So, somewhere in between EclipseLink 2.0.2 and 2.5.1 this issue/bug got fixed.

Tuesday, October 15, 2013

Testing locale-specific code without wiring up a Spring MessageSource

Today I needed to create a JUnit test for a validator class that was using a MessageSource to retrieve the message content I was sending back to the user. When I first ran my unit test, of course the message source was null. I was left wondering if I was going to have to create a test context XML file just to wire up the test (cries in the fetal position under desk).

Instead, I found an easier answer - Spring's StaticMessageSource implementation. You can create an instance of this class, and add messages to it (code, locale and message):

StaticMessageSource messageSource = new StaticMessageSource();
messageSource.addMessage("my.message.path", Locale.US, "My Message");


With this solution, I was able to unit test my validator code with needing to wire up the actual MessageSource implementation. Obviously this solution will not work if you're testing/validating the actual message content.

Monday, October 14, 2013

JAXBContext memory leak

Last Friday we had an Out Of Memory exception on one of our production WebLogic servers. The server only has 2 applications running on it, so at least the job of narrowing down the culprit would be easy. Or so we thought (note to self: find a better application monitoring/profiling solution!).

6 hours and many cups of coffee later, I realized that the developer that had written this application was using JAXB to marshal / un-marshal XML from a web service end point. As per the API example, the developer was creating a JAXB context for every message in this manner:

StringWriter sw = new StringWriter();
JAXBContext jaxbContext = JAXBContext.newInstance(myObject.getClass());
Marshaller marshal = jaxbContext.createMarshaller();
marshal.marshal(myObject, sw);

A new JAXB context was getting created every single time a web service message was sent/received! There is a known memory leak issue when using the JAXB context in this manner. Check out the While One Fork blog for more information.

I ended up creating a JAXBContextFactory class that allows the JAXBContext to be cached and reused. Find the code below. We redeployed the code and voila - the memory issues disappeared!
package my.mypackage.jaxb;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

public class JAXBContextFactory {
    private static JAXBContextFactory instance = new JAXBContextFactory();
    
    private static final Map< String, JAXBContext > instances = new ConcurrentHashMap< String, JAXBContext >();
 
    private JAXBContextFactory() {
    }
 
    /**
     * Returns an existing JAXBContext if one for the particular namespace exists, 
     * else it creates an instance adds it to a internal map.
     * @param contextPath the context path
     * @throws JAXBException exception in creating context
     * @return a created JAXBContext
     */
    public JAXBContext getJaxBContext(final String contextPath) throws JAXBException {
     JAXBContext context = instances.get(contextPath);
        if (context == null) {
            context = JAXBContext.newInstance(contextPath);
            instances.put(contextPath, context);
        }
        return context;
    }
 
 
    /**
     * Returns an existing JAXBContext if one for the particular namespace exists,
     * else it creates an instance adds it to a internal map.
     * @param contextPath the context path
     * @throws JAXBException exception in creating context
     * @return a created JAXBContext
     */
    public JAXBContext getJaxBContext(final Class contextPath) throws JAXBException {
        JAXBContext context = instances.get(contextPath.getName());
        if (context == null) {
            context = JAXBContext.newInstance(contextPath);
            instances.put(contextPath.getName(), context);
        }
        return context;
    }
 
    /**
     * Get instance.
     * @return Instance of this factory
     */
    public static JAXBContextFactory getInstance() {
        return instance;
    }
}


Here is how to use it in your application:
StringWriter sw = new StringWriter();
JAXBContextFactory factory = JAXBContextFactory.getInstance();
JAXBContext context = factory.getJaxBContext(MyClass.class);
Marshaller marshal = jaxbContext.createMarshaller();
marshal.marshal(myObject, sw);

Welcome!

I created this blog as a journal of the daily issues and findings I come across in my life as a Java developer. I'll not only discuss Java topics, but also Spring, jQuery, Oracle and various other technologies. I hope some of my postings can be of use in your own daily development struggles!