Struts2 action classes as we know should return always a string
But I always liked the idea of JAX-WS return result flexibilty. For returning an arbritrary string all you need to do it return
Recently I found out a way to return arbitrary result class which is not registered in struts.xml. This class gets injected at runtime in struts and performs the custom result creation.
So lets see some code and get a feel.
Please note this piece code was not tested, and for sake of completeness I just showed it here. It will also help understanding the struts was of handling custom result. You can get advantage of result name based flow definition which is integral part of struts. You might also get advantage of result class creation by struts bean factory and interceptors like workflow interceptors will also function correctly with this method. Thanks for following the post. Hope it helps someone.
"success", "error", "input"
which is used for navigation paths. There has to be an entry in struts.xml or via convention to tell struts what result handler to be used for a struts result nameBut I always liked the idea of JAX-WS return result flexibilty. For returning an arbritrary string all you need to do it return
String
The returned result is exactly what user sees on screen which is ideal for using struts as JSON data provider.
Recently I found out a way to return arbitrary result class which is not registered in struts.xml. This class gets injected at runtime in struts and performs the custom result creation.
So lets see some code and get a feel.
Action Class - TestAction.java
package actions; import myresults.MyResult; import org.apache.struts2.convention.annotation.Action; import com.opensymphony.xwork2.ActionSupport; public class TestAction extends ActionSupport { private static final long serialVersionUID = 1L; @Action("show") public MyResult show(){ System.out.println("This is executed in action class"); //returning arbritrary result handler instead of result names //like "success" or "error" or "someResultName" return new MyResult("This is from my result class"); } }As you can see here the return result is an arbitrary class MyResult. So lets see the MyResult class.
Arbitrary Result class - MyResult.java
package myresults; import java.io.PrintWriter; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import org.apache.struts2.dispatcher.StrutsResultSupport; import com.opensymphony.xwork2.ActionInvocation; public class MyResult extends StrutsResultSupport { private String text; public MyResult(String text){ this.text = text; } @Override protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { HttpServletResponse res = ServletActionContext.getResponse(); PrintWriter writer = res.getWriter(); writer.println(text); writer.close(); } }Other normal J2EE artifacts are below.
web.xml
struts2 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter actionPackages com.mycompany.myapp.actions struts2 /*
Maven - pom.xml
This is absolutely not necessary to build using maven.4.0.0 MyResultTest MyResultTest 0.0.1-SNAPSHOT war org.apache.struts struts2-convention-plugin 2.3.16.1 javax.servlet javax.servlet-api 3.1.0 org.apache.struts struts2-config-browser-plugin 2.3.16.1
Browser screenshot
Console screenshot
So far that was simplest result class. But using this technique a more useful results classes can be built. Like the one for outputting strings or json. So, I modified the classes again to create JSON serializer. I used quick json creation using org.json:json:20140107 maven jar, for performance streams can be used. But this will also work.TestAction.java
package actions; import java.util.HashMap; import java.util.Map; import myresults.MyJsonResult; import org.apache.struts2.convention.annotation.Action; import com.opensymphony.xwork2.ActionSupport; public class TestAction extends ActionSupport { private static final long serialVersionUID = 1L; @Action("show") public MyJsonResult show(){ System.out.println("This is executed in action class"); Mapmap = new HashMap (); Map address = new HashMap (); map.put("firstName", "samarjit"); map.put("lastName", "samarjit"); address.put("location", "Singapore"); address.put("road", "Cecil Street"); map.put("address", address); //returning arbritrary result handler instead of result names // like "success" or "error" or "someResultName" return new MyJsonResult(map); } }
MyJsonResult.java
package myresults; import java.io.PrintWriter; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.apache.struts2.ServletActionContext; import org.apache.struts2.dispatcher.StrutsResultSupport; import org.json.JSONArray; import org.json.JSONObject; import com.opensymphony.xwork2.ActionInvocation; public class MyJsonResult extends StrutsResultSupport { private String text; public MyJsonResult(Mapmap){ this.text = new JSONObject(map).toString(3); } public MyJsonResult(List <Object > lst){ this.text = new JSONArray(lst).toString(3); } @Override protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { HttpServletResponse res = ServletActionContext.getResponse(); res.setContentType("application/json"); PrintWriter writer = res.getWriter(); writer.println(text); writer.close(); } }
Result browser screenshot
The directory structure is very simpleMyResultTestProject │ ├───pom.xml │ ├───src │ ├───main │ │ ├───java │ │ │ ├───actions │ │ │ │ TestAction.java │ │ │ │ │ │ │ └───myresults │ │ │ MyJsonResult.java │ │ │ │ │ ├───resources │ │ └───webapp │ │ └───WEB-INF │ │ web.xml │ │ │ └───test │ ├───java │ └───resourcesAnd here is how it looks in eclipse.
Struts2 way of handling custom result
The above result class return type is something I accidentally found out from source code. However struts2 allows registering of custom result class in struts.xml in a new package or by extending struts-default package. Your action classes can then use it by returning a result name as you would have defined in struts.xml or in convention while defining action class. The result type has to be pre-configured either in annotation or in struts.xml in a new package.So in this example result name is "success" and result type is defined as "newresult". The actual class name for "newresult" was registered earlier.${jsonData}
Please note this piece code was not tested, and for sake of completeness I just showed it here. It will also help understanding the struts was of handling custom result. You can get advantage of result name based flow definition which is integral part of struts. You might also get advantage of result class creation by struts bean factory and interceptors like workflow interceptors will also function correctly with this method. Thanks for following the post. Hope it helps someone.