Wednesday, June 27, 2012

Jackson custom serialization/deserialization (for java.util.Date) in Jersey

Approach #1


Write custom serializer and deserializer


import java.io.IOException;
import java.util.Date;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

/**
 * Used to serialize Java.util.Date, which is not a common JSON
 * type, so we have to create a custom serialize method;.
 *
 */
public class JsonDateSerializer extends JsonSerializer<Date>{

    @Override
    public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
            throws IOException, JsonProcessingException {
        String formattedDate = DateAdapter.printDate(date);
        gen.writeString(formattedDate);
    }
   
}


 import java.io.IOException;
import java.util.Date;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class JsonDateDeSerializer extends JsonDeserializer<Date>{

    @Override
    public Date deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        System.out.println("JSON deserialization for " + jp.getText());
        return DateAdapter.parseDate(jp.getText());
    }

  
}

Annotate  each date field


Where ever date is declared, you annotate with following Jackson annotation specifying the serializer/deserializer to use

   @JsonSerialize(using=JsonDateSerializer.class, as = Date.class)
    public Date getCreatedOn() {
        return createdOn;
    }

    /**
     * Sets the value of the createdOn property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    @JsonDeserialize(using=JsonDateDeSerializer.class, as = Date.class)
    public void setCreatedOn(Date value) {
        this.createdOn = value;
    }

Note:

If you have getter/setters, annotations should NOT be given in fields but in getter/setter. See this.


Tuesday, June 26, 2012

Making Jersey to use Jackson instead of JAXB (server and client side)


Background


Jersey is a JAX-RS implementation which enables us writing REST resources by annotating interfaces/implementation in server side. Both XML and JSON data interchange format is supported by jersey. 
It uses JAXB for xml data interchange and JAXB again (with a JSON parser) for JSON interchange. JSON that's produced follows a specific notation - its JAXB based and can come with it's own issues:

a. array structure not formed in resultant JSON for single element arrays
e.g. {names : "Harry"} rather than {names : ["Harry"]}
b. exact enum name not used - name is taken from JAXB XML annotation

public enum AckValue {

    @XmlEnumValue("Success")
    SUCCESS("Success"),
    @XmlEnumValue("Failure")
    FAILURE("Failure")

produces {ack : "Success"} rather than {ack : "SUCCESS"}

These impose problems upon de-serializing JSON. Easy solution is to ask Jersey to use Jackson, an open-source, famous, so-far-best JSON parser (my opinion) for serialization which solves all stated issues above. But its not that easy.. 

Server side


Have Jackson jar (alone) in classpath


Make sure you have Jackson jar in classpath along with Jersey. Also for safety reasons, make sure any other JSON parser is not present, for e.g. Jettison. With multiple JSON parsers present in classpath, we can't guarantee that Jersey will pick Jackson. And with Jettison, not both of above issues can be resolved. For sample, at the time of writing, Jettison returns arrays with single element not enclosed in [] (issue 1).

Enable JSON POJO mapping

There are several JSON annotations that are available. POJO notation is one such which is vanilla POJO -> JSON -> POJO way and to use this we need to enable JSON POJO mapping (as mentioned in link above):

         <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
         </init-param>

Even with this, Jersey still might use JAXB for JSON serialization! Read further..

Jackson JAX-RS provider 

We might need to inject a custom provider to make Jersey use Jackson. Fortunately, we don't need to write one as one is already present in jackson-jaxrs jar (hence add this as dependency). Now to ask Jersey to use it, add the package "org.codehaus.jackson.jaxrs" to scan for it:

        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>org.codehaus.jackson.jaxrs;<your other packages with resources>   </param-value>
        </init-param>

Reference : SO post


Friday, February 18, 2011

Java memory analysis (OutOfMemoryError for example) tools

Just wanted to share the memory analysis tool that i happened to use recently when i hit an out of memory error in our application.. so far the experience has been good..

Memory analysis tool
(available as standalone tool or as eclipse plugin)

Tutorial on using MAT

Sun vs IBM JDK
Sun JDK generates heap dumps as .hprof files that are supported out of the box by MAT. 
If you are using IBM JDK then it generates .phd files that are not supported by MAT by default (atleast at time of this writing). 
But it can be resolved by installing IBM Diagnostic tool Framework. For more details and for download link, see

Saturday, January 1, 2011

java.lang.ClassCastException: com.google.apphosting.utils.jetty.AppEngineWebAppContext $AppEngineServletContext cannot be cast to org.mortbay.jetty.handler.ContextHandler$SContext

Quoting the solution from here,

Did you accidentally include appengine-local-runtime.jar in your
WEB-INF/lib?
So removing it solves the problem.

'GWT module may need to be recompiled' popup in browser

Quoting the solution from here,

This error usually means that you are missing the ?gwt.codesrv= (sp?)
parameter, probably gets lost in your redirect to the page
 Just get the full URL with ? from 'Development mode' tab in Eclipse after you start the server/run as web application and paste it in browser.
Or if you use 'ant devmode' say 'Copy to Clipboard' and paste it in browser.


Saturday, October 30, 2010

Workbench not coming while running UI tests for eclipse plugins in Hudson CI in windows

We planned to do CI setup for a project that involved eclipse plugins and decided to go to tycho for achieving that. The reason is simple as we were bit familiar with Maven and also saw that configuring to use tycho is easy (atleast we haven't used Ant before, so PDE build with Ant tasks, the usual way, is not gonna be easy as well).
This mini article is not about tycho but about overcoming the hurdle you might face when running eclipse UI integration tests written via WindowTester when setting up CI in Hudson in windows machine.

We first wrote a windows batch that does this -


a. Checkout projects from SVN
b. Trigger maven build, that builds plugins, runs unit tests followed by UI tests (that requires a launched workbench).

Tycho is brilliant is bringing up workbench right before running tests so things were going fine.
But when we moved to Hudson to set up, all happened except bringing up workbench. However an eclipse instance was created which was evident as seen in Task Manager but somehow workbench didn't come up which failed all UI tests.

Googling and googling finally fetched solutions in form of posts - UI tests in a CI env  - here, here and there and though they are not directly related to stated problem, the concept applies..

Read them? Fine, so what was the problem really?
We used to do the Hudson setup in a remote box which we used to login via mstsc console. As mentioned in above posts, doing remote login itself would lock the desktop and hence Hudson won't be able to interact natively with OS to bring up the workbench. This won't occur if you configure Hudson directly in your local machine i.e. when you logged directly into the machine where you are configuring Hudson. But this won't be the case as usually we configure Hudson in a remote box or one in a CI farm..

These are the points to note
a. Have a VNC server installed in the machine where Hudson has to be configured. We used RealVNC free edition.
b. Now in the remote machine (where we just installed VNC) where Hudson has to be configured disconnect all user sessions. Do it by going to Task Manager -> Users. No entry should be present and just disconnect all users. This implies that the machine should not be a normal box which is shared among users. Better disable remote session for the box.
c. Now remember this 'never use mstsc console to launch Hudson instance'. So connect to remote machine via VNC viewer installed in your local box. (you should give something like hostname:display to connect)
d. Once connected via VNC viewer, launch Hudson via command prompt; yes, not via Windows Service!
e. Now just trigger the build from local box or the remote box itself and see the workbench being shown up!

We haven't done any configuration in Hudson itself so far and things will be fine unless you would like have multiple builds being done in parallel by Hudson in same above machine and each build having to run UI tests as well..
Interesting..
Actually we came to know about a Hudson plugin named XVnc to satisfy exactly this purpose, but wait, we didn't play with it, as luckily we got a dedicated box for our project - so only one build at a time - so no prob..
The notion X protocol that the plugin talks about is specific to Linux based systems so not sure how easy to set it up in windows. We also contacted the plugin creator and got that the plugin was tested only in Linux systems (atleast until the time of this writing) but it doesn't mean that its not possible to set it up in windows. However we heard that minor tweaking might be required to see that the scripts that the plugin executes are intact in windows based machines too.. Anyways, hope we get a chance to experiment that too..

Monday, October 25, 2010

Adding test suites to a test suite in Junit 3.x

Normally in a junit 3.x test suite we will add test classes (that has our test methods) as entries. 
As an example,

public class AllTests {

    public static Test suite() {

        TestSuite suite = new TestSuite(
                "All tests wrapped in a suite");
        // $JUnit-BEGIN$
        suite.addTestSuite(SampleTest.class);
        suite.addTestSuite(AnotherSampleTest.class);
        // $JUnit-END$
        return suite;
    }

}

But what if we want to put several test suites into a master test suite.
It can be done in this way:

public static Test suite() throws Exception {
         TestSuite suite = new TestSuite(
                "Master test suite that has all test suites");
        // $JUnit-BEGIN$
        suite.addTest(AllTests.suite());
        suite.addTest(AnotherAllTests.suite());       
        // $JUnit-END$
        return suite;
    }


And last but not least, you can mix and match both.