January 17, 2013

JAX-RS / Jersey CORS Filter and JAXB List


I had to implement CORS functionnalities for cross domain javascript in my REST app.
I stumbled upon the excellent solution posted by Usul.

Basically, you have to implement a ContainerResponseFilter that will check request headers and set response headers :
 public ContainerResponse filter(ContainerRequest req, ContainerResponse contResp) {
 
        ResponseBuilder resp = Response.fromResponse(contResp.getResponse());
        resp.header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
 
        String reqHead = req.getHeaderValue("Access-Control-Request-Headers");
 
        if(null != reqHead && !reqHead.equals(null)){
            resp.header("Access-Control-Allow-Headers", reqHead);
        }
 
        contResp.setResponse(resp.build());
            return contResp;
    }

This solution worked great until I had to return a List of objects. In this particular case, the Response.fromResponse method returned a ResponseBuilder object in which I had lost my collection type, resulting in a nice exception saying that no MessageBodyWriter was found.

A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/json was not found

To bypass it, instead of using the nice Response.fromResponse method, I had to set the headers "manually" in the ContainerResponse object by accessing the header map :

public ContainerResponse filter(ContainerRequest req, ContainerResponse contResp) {
  contResp.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
  contResp.getHttpHeaders().add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
  
  String reqHead = req.getHeaderValue("Access-Control-Request-Headers");

  if (null != reqHead && !reqHead.equals(null)) {
   contResp.getHttpHeaders().add("Access-Control-Allow-Headers", reqHead);
  }
  return contResp;
 }

Doing it this way, response object isn't modified and type information isn't lost. JAXB is now able to marshall json or xml either in single object or collection case of return.

January 15, 2013

Fix Subclipse setup in RAD 7.0

My RAD subclipse setup was broken. I had to work on that computer, wokrpsace was already set up when I started to use it and that was fine for me. But now, time to commit... and I get a nice error popup saying that "This client is too old to work with working copy" (error message is pretty clear).

So what to do ? well,  for sure not what I did : install the latest subclipse version (1.8.x) without checking first the SVN server version. In fact, i should have install the 1.6.x subclipse plugin.

I then tried to install the 1.6.x version via the update site, but I guess that after that my RAD setup didn't like to have both subclipse version installed...

RAD 7.0.x doesn't make it easy to uninstall plugins, so here's what I did to fix it and have 1.6 subclipse plugin working :
  • Shut down RAD
  • Move all thee org.tigris.something jars and folders out of the plugins eclipse directory. That made both 1.6.x and 1.8.x versions not to work.
  • Restart RAD
  • Use the following item menu to check what's really missing : Help / Software updates / Manage configuration



  • From there I was able to 
    • Disable 1.8.x version (still don't know how to remove it entirely though)
    • Diagnose what jars were missing to make the 1.6.x work fine : before I put the right jars back to the plugin directory, the corresponding items were flagged with a red cross in the above snapshot (just like a java compilation problem). Using the Display properties link helped me to retrieve the "good" jars and make the red cross disappear.
What made this a bit painful was that I had to restart RAD workspace after each item fix : fix JNA library by replacing the jar > restart RAD > check that red cross disappeared > see cwhat's nex jar missing > fix xxx library ... > ...
Without restarting in between each fix, my workspace would crash. 


January 07, 2013

RAD 7 & WAS 6.1 : server1 open for e-business .... but stucked with "starting" status

I've probably been a bit too rude with my WAS 6.1 instance.
With no reason (or so it seems to me...), the server instance was starting (I had the "server open for e-business" sentence, and was able to access my app), but RAD wouldn't detect it (stucked in the "starting..." state, so not able to do anything inside RAD).

Switching the communication type in the server view from RMI to SOAP, restart the server, and then back to RMI solved my problem.

HTH.

January 03, 2013

Jersey : inject Form params into java custom object with @Inject

Working with Jersey, I had to handle search form parameters in a POST method.
Well, with the number of form parameters growing, adding @FormParam in the method signature isn't really a sustainable solution.

Starting to think about how to code this, and looking at how was jersey injecting stuff like Context objects, I was really happy to find out that this feature exists, but isn't really documented.

Yoryos Valotasios found this some times ago, saving me headaches, and published his home made solution that made him realize the feature already exist : http://blog.valotas.com/2011/01/resteasy-form-annotation-for-jersey.html

Thanks to him for saving me time !

Note, this is a Jersey feature, not a JSR 311 one.

So, to the point :

//Resource class
@Path("/customers")
public class CustomerResource { 

  @Post
  public Response addCustomer(@Inject CustomerFormParam customerForm) {
     // your code
  }
}

// The handler object
public class CustomerFormParam {

  @FormParam("firstName")
  private String firstName;
 
  @FormParam("lastName")
  private String lastName;
  ...
}

Well... that's it !

Neat isn't it ?