2

I use the following JavaScript/jQuery function to make a remote procedure call.

<script src="../js/jquery-1.8.0.min.js" type="text/javascript"></script>
<s:url var="testJsonUrl" action="testJsonAction"/>

var timeout;
var request;

$(document).ready(function(){
    $("#btnUser").click(function(){
        if(!request)
        {
            request = $.ajax({
                datatype:"json",
                type: "GET",
                data: JSON.stringify({jsonrpc:'2.0', method:'getUser', id:'jsonrpc'}),
                contentType: "application/json-rpc; charset=utf-8",
                url: "<s:property value='#testJsonUrl'/>",
                success: function(response)
                {
                    var user = response.result;
                    alert(JSON.stringify(user));  //Always alerts "undefined".
                },
                complete: function()
                {
                    timeout = request = null;
                },
                error: function(request, status, error)
                {
                    if(status!=="timeout"&&status!=="abort")
                    {
                        alert(status+" : "+error);
                    }
                }
            });
            timeout = setTimeout(function() {
                if(request)
                {
                    request.abort();
                    alert("The request has been timed out.");
                }
            }, 30000);
        }
    });
});

The above function is called, when a button is clicked as follows.

<s:form namespace="/admin_side" action="Test" validate="true" id="dataForm" name="dataForm">
    <input type="button" name="btnUser" id="btnUser" value="Click"/>
</s:form>

The action class in which the method is to be invoked is as follows.

import com.opensymphony.xwork2.ActionSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.apache.struts2.json.annotations.SMDMethod;
import util.User;

@Namespace("/admin_side")
@ResultPath("/WEB-INF/content")
@ParentPackage(value = "json-default")
public final class TestAction extends ActionSupport
{
    private User user;

    public TestAction() {}

    @SMDMethod
    public User getUser()
    {
        user = new User();
        user.setName("Tiny");
        user.setLocation("India");

        try {
            user.setDob(new SimpleDateFormat("dd-MMM-YYYY").parse("29-Feb-2000"));
        } catch (ParseException ex) {
            Logger.getLogger(TestAction.class.getName()).log(Level.SEVERE, null, ex);
        }
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    @Action(value = "testJsonAction",
    results = {
        @Result(type = "json", params = {"enableSMD", "true", "excludeNullProperties", "true"})},
    interceptorRefs = {
        @InterceptorRef(value = "json", params = {"enableSMD", "true"})})
    public String executeAction() throws Exception {
        return SUCCESS;
    }

    @Action(value = "Test",
    results = {
        @Result(name = ActionSupport.SUCCESS, location = "Test.jsp"),
        @Result(name = ActionSupport.INPUT, location = "Test.jsp")},
    interceptorRefs = {
        @InterceptorRef(value = "defaultStack", params = {"params.acceptParamNames", "", "params.excludeMethods", "load", "validation.validateAnnotatedMethodOnly", "true"})})
    public String load() throws Exception {
        //This method is just needed to return a view on page load.
        return ActionSupport.SUCCESS;
    }
}

The User class:

public class User
{
    private String name;
    private Date dob;
    private String location;

    //Setters & getters.
}

The getUser() method (in the action class TestAction) annotated with @SMDMethod is expected to be invoked, when the given button is clicked but it does not.

This line alert(JSON.stringify(user)); in the success handler of the jQuery function always alerts undefined.

Using a direct link in the address bar like,

http://localhost:8080/TestStruts/admin_side/testJsonAction.action

returns the following string.

{
    "methods": [{
        "name": "getUser",
        "parameters": []
    }],

    "serviceType": "JSON-RPC",
    "serviceUrl": "\/TestStruts\/admin_side\/testJsonAction.action",
    "version": ".1"
}

What is missing here? Why does not JSON-RPC work?

PS : I'm using Struts2-json-plugin-2.3.16 with same version of the framework.

Tiny
  • 27,221
  • 105
  • 339
  • 599
  • Do you have a folder name admin_side? – Roman C Jan 26 '14 at 21:39
  • Yes there is a folder `admin_side` where JSP pages are placed. Its location is `WEB-INF/content/admin_side`. – Tiny Jan 26 '14 at 21:41
  • Could you change namespace of the action to "/"? – Roman C Jan 26 '14 at 21:56
  • Or better idea is change the name of the method to something else "doSomething". – Roman C Jan 26 '14 at 22:01
  • The method was renamed to `doSomething()` and the URL was changed to `` that made no difference. – Tiny Jan 26 '14 at 22:13
  • @RomanC : I'm still unable to solve the issue. Which jQuery library do you use for jQuery functions to work? I use `jquery-1.8.0.min.js`. – Tiny Jan 28 '14 at 13:00
  • I tested it with this version also, but loading from google. – Roman C Jan 28 '14 at 13:53
  • Once again, I have tried removing security and It worked but only with the `POST` method. Is it mandatory to have only the `POST` request while using `JSON-RPC`? Also `includeProperties` has no effect. When I try, `"includeProperties", "user\\[\\d+\\]\\.name, user\\[\\d+\\]\\.location"`, all the properties namely `name`, `dob` and `location` from the `User` class are received in the response (the field `dob` in this case, should have been excluded). – Tiny Jan 28 '14 at 15:30

1 Answers1

1

Is it mandatory to have only the POST request while using JSON-RPC?

yes, it only works with POST request

Also includeProperties has no effect

includeProperties has effect if it's a parameter to json interceptor. Don't confuse the name of parameter used with json result.

the field dob in this case, should have been excluded

In this case better to use excludeProperties parameter to interceptor, like this

@Action(value = "testJsonAction",
results = {
    @Result(type = "json", params = {"enableSMD", "true"})},
interceptorRefs = {
    @InterceptorRef(value = "json", params = {"enableSMD", "true", "excludeProperties", "result\\.dob"})})
public String executeAction() throws Exception {
    return SUCCESS;
}
Roman C
  • 49,761
  • 33
  • 66
  • 176
  • This worked indeed. I incorrectly assumed that the parameters of `includeProperties` were to be set to `user`. I however, had to use this regx (as you mentioned in one of the answers), `"result\\[\\d+\\]\\.stateId, result\\[\\d+\\]\\.stateName"` while retrieving a list of states from the database. Simply escaping a dot `.` like `"result\\.stateId, result\\.stateName"` resulted in fetching an empty list in the response. I have now a new issue still remaining which is about Spring Security!!! Sorry, I have given you a lot trouble! – Tiny Jan 28 '14 at 17:54
  • Can we have a parameterized `@SMDMethod` method? Referring to the example in the question, can we pass parameters to the `getUser()` method? This [wiki](https://cwiki.apache.org/confluence/display/WW/JSON+Plugin) (the last section, JSON-RPC) shows examples but it uses the dojo plugin. – Tiny Jan 28 '14 at 18:32
  • The `result` is a root object is being serialized, and it's the object that the method returns. In this example it's a `User` bean, so the regex will be just dot reference to a property. If the return object is List then it will be like you wrote, didn't test it. Parameters should be passed with `parameters:[]` and there's `@SMDMethodParameter`. – Roman C Jan 28 '14 at 18:35
  • `data: JSON.stringify({jsonrpc:'2.0', method:'getUser', id:'jsonrpc', parameters:[{'userId':1}]})` and in the action class, `public User getUser(@SMDMethodParameter(name="userId")Long userId){...}` says, `SEVERE: Method getUser could not be found in action class.` I'm missing something obvious. – Tiny Jan 28 '14 at 18:50
  • is this a valid json? – Roman C Jan 28 '14 at 18:58
  • JSON works, when this parameter `userId` is not used anymore i.e it works with `data: JSON.stringify({jsonrpc:'2.0', method:'getUser', id:'jsonrpc'})` and there is a parameterless method in the action class like `public User getUser(){...}`. – Tiny Jan 28 '14 at 19:04
  • Ok, use parameters as array of object values: `params:[1]` for example. – Roman C Jan 28 '14 at 20:09