JimmyblyLee 2011-01-07
Struts 2 + GWT
StrutsGWTPluginistherecommendedwaytointegrateStruts2andGWT.Usethistutorialifyoucannotchangeyouractions,oryoujustdon'tliketheway(GWTway)thatyouhavetoimplementthemtobeusedwiththeplugin.
ThesetutorialwilldemonstratehowtocallanStruts2action,usingGWTtosubmitaformandusethereturneddata.
Writeyouraction.
Exampleaction:
"Hello.java"package example; import com.opensymphony.xwork2.Action; public class Hello { private String firstName; private String lastName; public String execute() { return Action.SUCCESS; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
Writethemappingforyouraction.
Inthisexamplethemappingisinstruts.xml:
struts.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="example" extends="struts-default"> <action name="Hello" class="example.Hello"> <result type="json" /> </action> <action name="Main" class="com.opensymphony.xwork2.ActionSupport"> <result>org.apache.struts.gwt.Main/Main.jsp</result> </action> </package> </struts>
The "Hello" action has a result of type "json", which will serialize the action object into a JSON string. If "firstName" is set to "John" and "lastName" is set to "Galt", the output of the "Hello" action will be:
{
"firstName":"John",
"lastName":"Galt"
}
The"Main"actionpointstothe"Main.jsp"pagewhichisthepagethatusesGWT.Thepathoftheresultis"org.apache.struts.gwt.Main/Main.jsp".ThatmeansthatthefilesgeneratedbyGWTmustgounderafoldernamed"org.apache.struts.gwt.Main"undertherootofyourapplication.See"Deploymentstructure"formoredetails.
Write"Main.jsp"page.
ThisisthepagethatisgeneratedbyGWT's"applicationCreator".Ithasbeenrenamedto.jspbecausewehavemodifiedittobeajsppage,insteadofaplainhtmlpage.
Main.jsp<%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <s:head theme="ajax" debug="true"/> <meta name='gwt:module' content='org.apache.struts.gwt.Main/org.apache.struts.gwt.Main'> </head> <body> <script language="javascript" src="${pageContext.request.contextPath}/org.apache.struts.gwt.Main/gwt.js"/> <form id="form1"> <input type="text" name="firstName"> <br/> <input type="text" name="lastName"> <span id="slot1"></span> </form> <br/> <span id="slot2"></span> </body> </html>
Wesethead'stagattribute"theme"toajaxtouseDojo,don'tpanic,youwon'thavetouseitdirectly.NotethatwehavechangedafewthingsfromtheoriginalhtmlpagegeneratedbyGWT,weset"content"to"org.apache.struts.gwt.Main/org.apache.struts.gwt.Main"becausetheGWTgeneratedfileswillbeunder"AppRoot/org.apache.struts.gwt.Main"insteadofbeneathroot,andweset"src"to"${pageContext.request.contextPath}/org.apache.struts.gwt.Main/gwt.js"forthesamereason.WithoutthesetwochangestheGWTfileswouldn'tbeloaded.
Struts2GWTHelper
Thisclasswilltakecareofmakingtherequest.Why?WhydoIneedthisclass?Coupleofreasons,first,thereisabugonbugonHTTPRequest.asyncPostwhichdoesn'tencodethepayloadproperly,second,ifyouwanttosubmitaform,youhavetoencodeityourself,andthisclasswillhelpyoudothat.Optionallyyoucandownloadajarcontainingthisclass(withmoremethods)fromhere,addthistoyourGWTapplicationfile(i.eMain.gwt.xml):
<inheritsname='struts2gwt.Struts2GWT'/>
andaddthejartotheclasspathinyourcompilescript(i.eMain-compile.cmd)andthecompilescript(i.eMain-shell.cmd).
Struts2GWTHelper .javapackage org.apache.struts.gwt.client; import com.google.gwt.user.client.ResponseTextHandler; public class Struts2GWTHelper { /** * Make asynchronous post * @param url Action url * @param formId id of form that will be posted * @param handler callback function */ public static native void asyncPost(String url, String formId, ResponseTextHandler handler) /*-{ dojo = $wnd.dojo; //don't use the dojo.io.bind({...}) shortcut, it doesn't work here var request = new dojo.io.Request(url); request.load = function(type, data, request) { [email protected]::onCompletion(Ljava/lang/String;)(data); }; request.formNode = dojo.byId(formId); request.method = "POST"; $wnd.dojo.io.bind(request); }-*/; /** * Make asynchronous post * @param url Action url * @param handler callback function */ public static void asyncPost(String url, ResponseTextHandler handler) { Struts2GWTHelper.asyncPost(url, handler); } }
See?Itwasn'tthatbad.
WriteyourGWTentrypoint
Main.javapackage org.apache.struts.gwt.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.json.client.JSONObject; import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONString; import com.google.gwt.user.client.ResponseTextHandler; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.ClickListener; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class Main implements EntryPoint { final Label label = new Label(); /** * This is the entry point method. */ public void onModuleLoad() { final Button button = new Button("Click me"); button.addClickListener(new ClickListener() { public void onClick(Widget sender) { makeRequest(); } }); RootPanel.get("slot1").add(button); RootPanel.get("slot2").add(label); } private void makeRequest() { Struts2GWTHelper.asyncPost("Hello.action", "form1", new ResponseTextHandler() { public void onCompletion(String responseText) { JSONObject obj = (JSONObject)JSONParser.parse(responseText); JSONString firstName = (JSONString)obj.get("firstName"); JSONString lastName = (JSONString)obj.get("lastName"); label.setText("Welcome " + firstName.stringValue() + " " + lastName.stringValue()); } }); } }
The makeRequest() method will make a request to the "Hello" action that we created before, and will pass the fields of the form "form1" as parameters. Which will be populated on the action, and serialized back as JSON. Using JSONParser the JSON string is parsed into a JSON object. It is definitely not type safe as GWT Remoting, but it works.
CreateMain.gwt.xml
Nothingnewonthisfile,justasreference:
Main.gwt.xml<module> <!-- Inherit the core Web Toolkit stuff. --> <inherits name='com.google.gwt.user.User'/> <inherits name='com.google.gwt.json.JSON'/> <!-- Specify the app entry point class. --> <entry-point class='org.apache.struts.gwt.client.Main'/> </module>
Deployment structure
FolderNotes
AppRootTheapplicationdeploymentfolder
AppRoot/index.htmlWelcomePage
AppRoot/org.apache.struts.gwt.MainContentgeneratedbyGWT(usuallyitgetsgeneratedintoafoldernamed'www')
AppRoot/WEB-INFRegularwebappfilesandclasses