Thursday, August 16, 2012

Struts2 json plugin SMDAction Tutorial


Note: This official document of struts2-json-plugin shows a way to call javascript rpc using dojo, but after slight googling I could not find a way to do so in jQuery. If anyone else faces the same challenge then this document might come in handy.

When I first looked at struts2 json plugin, I thought this was clearly going to create a bloat in the action class. This is because multiple action methods are supported in an action class, but all the properties in an action class gets serialized, unless we specifically go and stop them. Each action method deals with different arguments and different result variables. Spring MVC does a good job by specifying the variables associated with each action method and what object gets sent back. This clear cut definition helps to understand concern of each action method and keeps low on the the global variables in an action class which often raises to confusion.

 @RequestMapping(value="{name}", method = RequestMethod.GET)
 public @ResponseBody Shop getShopInJSON(@PathVariable String name) {
 
  Shop shop = new Shop();
  shop.setName(name);
  shop.setStaffName(new String[]{"mkyong1", "mkyong2"});
 
  return shop;
 
 }
One way to implement this is use required constraint validation for each variable on each action method, but still the global variables count rises. Other options is to split the actions into different action classes. I was thinking of creating the same annotations in struts which are available in springs. But, wait, someone has done something good, and here comes SMDAction, it does somewhat the same this as springs.

@ParentPackage("json-default")
public class SMDAction {
    
    @Action(value="SMDAction",
      interceptorRefs={
       @InterceptorRef(value="json",params={"enableSMD","true"})
      },results={
       @Result(type="json",params={"enableSMD","true"})
    })
    public String smd() {
        System.out.println("smd() executed");
        return "success";
    }

    /**
     * Taken from struts2-json-plugin documentation
     * We know exactly what arguments gets passed to my action.
     * We also know what object gets serialized back and returned.
     */
    @SMDMethod
    public JSONObject doSomething(JSONObject bean, int quantity) 
    throws Exception {
      System.out.println("smd.doSomething() executed with input "+
            bean.toString());
        bean.put("mykey",10);
        bean.put("mykey2",20);
        JSONArray jar = new JSONArray();
        jar.add("sam");
        jar.add("sambin");
        bean.put("bean", jar);
      
        return bean;
    }
}
The reason why I said it nearly does something similar to spings MVC because, it skips the validation portion. Well I seldom use struts validation interceptor anyway, and I serialize action erros, field errors and action messages myself. So lets look at how the above works. The example in struts2-json-plugin documentation was using dojo.rpc*. Again I do not use dojo. So lets see how it can be done in jQuery. Things that do not work
  1. $.post(), $.get() and other helper methods does not work as you cannot set the content-type of the returned stream.
  2. This also does not accept form data like the usual url = '&key=value' pairs. in url or post data
Things that works
HTTP Request: Use $.ajax to send data as stream. Remember to set the content-type='javascript/json-rpc' HTTP Response: Is a standard json object. How it works
Just like in WebServices, it is possible to get the service definition. Just query with a different content type. It will return the structure of the RPC service, the parameters it takes and the version, request id etc.
$.ajax({
 type: 'POST',
    url:'SMDAction.action',
    data: '{"params":[{"type":"Mocca"},5],"method":"doSomething","id":5}',
    contentType:"application/json" ,
    success: function (d){ console.dir(d);}
});

Result RPC service definition:
{"methods":[{"name":"doSomething",
"parameters":[{"name":"p0"},{"name":"p1"}]}],
"objectName":null,
"serviceType":"JSON-RPC",
"serviceUrl":"\/stryts2json\/SMDAction.action",
"version":".1"
}
Basically this gives enough information to anyone who wants to call the actual RPC. To actually do the remote procedure call. Note two things.

Important Note:

  1. ajax parameter data: '{}' -> The single quotes are important! If you do not put the single quotes then this is submitted as form values like key-value pairs. But the required format is stream, so pass it as a string
  2. contentType: "application/json-rpc" -> This is also extremely important, if you pass anyother content-type then the rpc method schema is returned instead of calling the actual rpc method.
$.ajax({
 type: 'POST',
    url:'SMDAction.action',
    data: '{"params":[{"type":"Mocca"},5],"method":"doSomething","id":5}',
    contentType:"application/json-rpc" ,
    success: function (d){ console.dir(d);}
});
Result of RPC:
{"debug":null,
"error":null,
"id":"5",
"result":{"type":"Mocca","mykey":10,"mykey2":20,"bean":["sam","sambin"]}
}
This looks really neat. Hope you guys like it and use it more frequently.

No comments:

Post a Comment