Labels

Showing posts with label Jax-RS. Show all posts
Showing posts with label Jax-RS. Show all posts

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 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.