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 ?

December 28, 2012

Chrome extension to test RESTFul services

Hi all

Just found a great tool to quickly build HTTP requests to test REST Services : Postman is its name.

Web interface is neat and simple, you can store your requests under Collections, control headers, follow "rel" style links, and so on.

The tool supports authentication as well, I tested quickly simple auth and it works great.

On top of that, you can dynamize your requests with some attributes, i.e. test the same request against several environments using a http://{{serverName}}/rest/... syntax.

December 18, 2012

Jax-RS Jersey on WAS 6.1 : ASM ClassReader noSuchMethodException

Today I had to POC a Jersey REST application deployed on a Websphere Application Server 6.1.

At first I had the now classic Hibernate / Jersey exception related to asm dependency :


[13/12/12 14:36:32:296 CET] 00000033 ServletWrappe E   SRVE0100E: Exception init() non intercept?e par le servlet ServletContainer : java.lang.NoSuchMethodError: org/objectweb/asm/ClassReader.accept(Lorg/objectweb/asm/ClassVisitor;I)V
 at com.sun.jersey.server.impl.container.config.AnnotatedClassScanner.analyzeClassFile(AnnotatedClassScanner.java:322)
 at com.sun.jersey.server.impl.container.config.AnnotatedClassScanner.indexDir(AnnotatedClassScanner.java:271)
....
...

The thing is :

  • Hibernate is loading cglib, which in turns loads asm with an old version
  • Jersey needs asm with a newer version, and both version aren't compatibles because of changes in packages.
Solution (that's not from me, it's well documented on the network) :
  • Exclude cglib, asm and asm-attr when declaring hibernate maven dependency (if you're using maven of course)
  • Explicitely declare a dependecy to cglib-nodep. This cglib repackaged artefact includes cglib AND asm, with altered package definition not to conflict with jersey asm version.
So your poms should by now look like this :



   cglib
   cglib-nodep
   2.1_3




   org.hibernate
   hibernate
   
   
   
    
     asm
     asm
    
    
     asm
     asm-attrs
    
    
     cglib
     cglib
    
   





   com.sun.jersey
   jersey-server
   1.1.4.1






So now hibernate is using is own version of asm, and Jersey the 3.11 required version.
So why do I keep getting this error ?

After a few hours struggling with my favorite deployment manager, which I thought was the cause of the issue, I realized that the loaded ClassVisitor class was coming from WAS internal plugins.
After setting a breakpoint and inspecting the classloader, I saw that the class was loaded from this jar : 
C:/Program Files/IBM/SDP70/runtimes/base_v61/plugins/com.ibm.wsfp.main_6.1.0.jar

So what's left to us ?
  • Solution #1 : Delete this jar :-) - Works, but not optimal !
  • Solution #2 : There must be a Websphere setting related to the classloader (PARENT_FIRST vs PARENT_LAST ?), but I can't get it to work (I get exceptions in the was console whenever I try to change those settings).

Anyhow, if anybody knows how to tell Websphere to pick up my jar instead of its jar, please let me know ! Any help would be greatly appreciated.

December 14, 2012

Multiple SSL Virtual Host with Apache : the SNI isssue

A short note of what I understood.

For french speaking persons, have a look here : Mimiz explained that much better than I do !

Before Apache 2.2.12, if you set up multiple Virtual Host with SSL configuration, you owuld end up with a warning like this :


[warn] vhost2 VirtualHost overlap on port 443, the first (vhost1) has precedence

You could set up as many SSL VHost configuration you want, on ly the first one would be used, sometime resulting in client warnings because wrong certificate was served.

From 2.2.12 and on, OpenSSL (shipped with Apache) uses the SSL extension named SNI (which stands for Server Name Identification). This extension allows Apache to send the right certificate, according to the domain requested.

But client AND server have to use this SNI thing, and of course some clients do not follow the rule...follow my gaze.

According to the wiki page :

No support

The following combinations do not support SNI:

In short, If you have to support IE on XP, you'll have to have as many IP addresses as desired domains.