JSON serialization (using Jquery plugin) w/ Struts2 interceptors - - json

I'm trying to implement http://code.google.com/p/struts2-jquery/wiki/SelectTag#A_simple_Doubleselect_with_Topics but I can't seem to combine the json interceptor with other interceptors successfully.
In my struts.xml:
<package name="admin" namespace="/admin" extends="struts-default,json-default">
<action name="LoadLists" method="loadLists" class="test.JSONAction">
<interceptor-ref name="json">
<param name="contentType">application/json</param>
<!--interceptor added to override this property below-->
<param name="excludeNullProperties">true</param>
</interceptor-ref>
<result name="success" type="json"/>
<interceptor-ref name="servletConfig"/>
</action>
</package>
Here's some of the action class code.
Note that I need the session variable and therefore have added the <interceptor-ref name="servletConfig"/> line above to set the session variable so that it can be used in the following Java code:
public String loadLists() {
items = (List<String>) session.get("itemsList");
if (itemSelected.equals...
// Do stuff to process the list and generate the second list...
}
public void setItemSelected(String itemSelected) {
this.itemSelected = itemSelected;
}
BUT when I have <interceptor-ref name="servletConfig"/>, error logs show:
org.apache.struts2.json.JSONInterceptor.debug:68 - Content type must be 'application/json' or 'application/json-rpc'. Ignoring request with content type application/x-www-form-urlencoded
and the variable itemSelected never gets set because the json serialization is ignored!
If I remove <interceptor-ref name="servletConfig"/> then I can't access the session!
What am I missing?

The problem here appears to be that you are not making an AJAX/JSON request to your action, you are posting to it using a standard form approach.
The message that you have provided is saying that the Content-Type request header was expected to be JSON-related, but instead was x-www-form-urlencoded. In other words, the request was not an AJAX/JSON request, but was just a normal form submit.
Double check how you are making the request to your JSONAction and make sure that you are actually sending the request properly.

Related

Struts2 Only Use Specific Variable Object for JSON Result (not all Action variables)

Suppose my Struts mapping returns a JSON string,
<action name="retrieveJson" method="retrieveJson" class="myapp.WebServiceAction">
<result type="json">
<param name="contentType">text/plain</param>
</result>
</action>
My Action class has multiple variables that could be "construed" as the potential result.
public class WebServiceAction {
private List<PublicationRecord> publicationRecords; // getters+setters
private List<ReviewRecord> reviewRecords; // getters+setters
private List<CustomRecord> customRecords; // getters+setters
}
When I do the following, I set the particular variable that I want, but Struts2 seems to return all variables under the Action that are suitable:
public String retrieveJson() {
publicationRecords = service.getPublicationRecords();
return SUCCESS;
}
Is it wrong to return SUCCESS? I only want the JSON-ified variable that I set in this method. Right now, it's returning all 3 vars,
{
"publicationRecords" : ..,
"reviewRecords" : null,
"customRecords" : null
}
Expected:
{"publicationRecords" : .. }
For this you could use 2 properties.
excludeNullProperties or includeProperties for serializing only the desired fields. Also includeProperties allow the use of regular expressions in case you do not want to serialize the full object content.
<result type="json">
<param name="includeProperties">
^entries\[\d+\].clientNumber,
^entries\[\d+\].scheduleNumber,
^entries\[\d+\].createUserId
</param>
</result>
Here is the official documentation.

Can't access JSON output via URL

I get the service to respond when going to the URL /Resources/Feeder.svc on my localhost. However, I can't access /Resources/Feeder.svc/ping, although I'm using the following code.
[ServiceContract]
public interface IFeeder
{
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "ping")]
[OperationContract]
string Ping();
}
public class Feeder : IFeeder { public string Ping() { return "pung"; }
I've been trying with and without a bunch of attributes, trying to access the service with and without svc etc. Nada. I'm drawing blank.
I've tried to follow a guide or two but I simply can't see what I'm missing.
I'm getting empty hits - no text at all. The error says 400 bad request. Tells me nothing... What can I do to debug it? Most likely it's something really stupid because I'm tired. Sorry about that...
You haven't shown your web.config and my guess would be that you probably forgot to edit it. You need to do two things. First of all, declare an endpoint behavior. Second, add a protocol mapping. Like this.
<system.serviceModel>
<!-- Addition of protocol mapping -->
<protocolMapping>
<add scheme="http" binding="webHttpBinding"/>
</protocolMapping>
<!-- End of addition -->
<behaviors>
<!-- Addition of endpoint behavior -->
<endpointBehaviors>
<behavior>
<webHttp />
</behavior >
</endpointBehaviors>
<!-- End of addition -->
...
</behaviors>
...
</system.serviceModel>
Also, I don't think you actually need the attribute OperationContract if you're using WebGet. It's redundant.

Getting json return type and html return in the same action

I'll try be as specific as possible.
I have an Action with two methods, one is called via ajax and other one via regular submit.
The point is that can't get the request from the regular submit, I'm getting only the action properties.
public class ClientAction{
#SMDMethod
public Map<String, Object> findClient(String myParam){
...
}
public String saveClient(){
Map<String, String[]> parameterMap = this.getRequest().getParameterMap();
}
}
getRequest from saveClient method returns null!!! But, why??? I didn't declare it with #SMDMethod
and here is the struts.xml
<action name="client" class="myCompany.ClientAction">
<interceptor-ref name="customJSON"><param name="enableSMD">true</param></interceptor-ref>
<result type="json"><param name="enableSMD">true</param></result>
</action>
I did all the others declarations. I used to have two separete classes, one for each method, but maintainability wasn't easy with ClientAction and ClientActionJSON.
Any thoughts on how to have both methods, one ajax and the other not, in the same class.
I'll straight away consider to write a sample :
<action name="xclient" class="myCompany.ClientAction" method="jsonMethod">
<result type="json"></result>
</action>
<action name="yclient" class="myCompany.ClientAction" method="htmlMethod">
<result type="dispatcher">/pages/y.jsp</result>
</action>
now simply create both methods jsonMethod() & htmlMethod() in your ClientAction, one handling json and another html response.
[EDIT]
I read it again and seems like you require only one-action, well then simply consider using a field (request parameter) to decide the return type.
public String execute(){
//..Other code
if(returntype.equals("json")){
return "jsonresult";
}
else{
return "htmlresult";
}
}
<action name="client" class="myCompany.ClientAction" method="jsonMethod">
<result name="jsonresult" type="json"></result>
<result name="htmlresult" type="dispatcher">/pages/y.jsp</result>
</action>
Above I assumed, returntype is a String variable which you sent along with each request specifying what return is expected. You can simply send it hidden in the form-submit and set it in the ajax-request.

Struts2 JSON Plugin with Hibernate

So I'm trying to create a JSON object from a List of AcaClasses.
Action Class:
public class StudentJSONAction extends ActionSupport{
//Your result List
private List<AcaClass> gridModel;
public String getJSON() {
return execute();
}
public String execute() {
//Get the first student from the Factory and get their AcaClasses
gridModel = StudentFactory.getAll().get(0).getAcaClasses();
return SUCCESS;
}
//Getters and Setters
The StudentFactory is my interface to the hibernate database.
Struts.xml
<action name="getJSON" class="StudentJSONAction">
<result type="json">
<param name="enableSMD">true</param>
<param name="ignoreInterfaces">false</param>
</result>
</action>
When I call the getJSON action, all I get is:
{"methods":[],"objectName":null,"serviceType":"JSON-RPC","serviceUrl":"\/FlowridersSP\/getJSON","version":".1"}
This problem is very similar to mine but I would like to see if there is a solution using the Struts2 JSON Plugin
Question: Why am I not getting back a list of AcaClasses in JSON form?
My end goal is to plug in this JSON in the JQuery Grid Plugin
I am not familiar with JSON plugin, but are you correctly configuring the plugin to serialize the gridModel? A quick look at the plugin documentation suggests that you might want to set the root parameter also:
<action name="getJSON" class="StudentJSONAction">
<result type="json">
<param name="enableSMD">true</param>
<param name="ignoreInterfaces">false</param>
<param name="root">gridModel</param>
</result>
</action>
Also, try to identify if the problem is with StudentFactory or with the JSON serialization. You can set up gridModel with a list of dummy AcaClass objects and see if the serialization works correctly. Also, as suggested by #Quaternion, you can log the list of AcaClass objects loaded by StudentFactory and verify that it is loading the expected instances.

Struts2 Application hides my exceptions after adding Interceptor

So I have a Struts2 application that I'm working on. On my front page I have a section that will display any exceptions my application throws. This worked well until I added a custom Interceptor.
Here is my interceptor code:
public String intercept(ActionInvocation actionInvocation) throws Exception {
String result = actionInvocation.invoke();
return result;
}
This is the code in my Action class where the exception gets generated, it occurs where AuthService.Authorize() is called:
if(AuthService.Authorize(username, password)) {
if(AuthService.AdminAuthorized()) {
return "admin";
}
return SUCCESS;
}
This is inside of AuthService.Authorize(), it throws a null point exception when acc is accessed :
try {
acc = profileRepository.WhereSingle("Username", "=", username);
} catch (Exception e) {
return false;
}
if (acc.Password.equals(password)) {
However, when the page is loaded. This is not populated:
<s:property value="%{exception.message}"/>
I have tested it and it would work if I was simply throwing an exception from the Action class. I am not calling a redirectAction or anything
Here is the top of my default package definition which all my other packages extend
<package name="default" namespace="/" extends="struts-default">
<!-- Interceptors -->
<interceptors>
<interceptor name="conversation" class="global.ConversationInterceptor"/>
<interceptor-stack name="dils-stack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="conversation"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="dils-stack"/>
<global-results>
<result name="Exception" >/index.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception" result="Exception"/>
<exception-mapping exception="java.lang.NullPointerException" result="Exception"/>
</global-exception-mappings>
How is your interceptor stack defined for that action? The ExceptionMappingInterceptor should be defined first in the stack. Can you post the interceptor stack configuration from your struts.xml? Your custom interceptor should not be interfering (it does nothing).
Updated:
I was able to reproduce this issue, however it occurs for me with or without your custom interceptor.
The reason is that you are specifically looking for the exception message, which is not set for NullPointerExceptions that are automatically thrown (as in your case). You can confirm this by instead displaying the stack trace, such as: %{exceptionStack}
%{exception.message} is null for the NullPointerException, and so it displays nothing. If instead you were to throw an exception with a message (e.g., throw new RuntimeException("OMG!");), then you will see the message.
Also, note that you must specify more specific exception mappings before less specific mappings in your struts.xml. Because NullPointerException is more specific than Exception, you must list it first. Note that this doesn't really matter in your example, because they map to the same thing. Just know that your NPE will map to the first entry, not the second.