Tutorial
Since Seasar2 supports HOT deploy, modifications made on code are applied on the testing application server wthout restarting. Besides, in most cases, it is not necessary to write configuration files.
Start up the application server following the instructions at Setting up Tutorial. Modify JAVA code or *.properties flie variously to feel the power of HOT deploy.
index
Open http://localhost:8080/sa-struts-tutorial with your web browser. You see a showcase page for various applications.
Let's see contents under webapp (i.e. web application root) directory. There is no index.jsp(index.html) present. Then, what page is displayed?
If a class named <rootpackage>.action.IndexAction is present, then SAStruts automatically invokes it when you access the web application root. For details of rootpackage, please see here.
Source code for IndexAction.java is as follows :
IndexAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; public class IndexAction { @Execute(validator = false) public String index() { return "index.jsp"; } }
After the transition to every action, an execution method with @Execute annotation is invoked. Please see here to find out which one of multiple execution methods defined is invoked. In this case, we have only one execution method, and obviously, index() is called.
The return value of the method specifies where to forward. In the case of IndexAction#index(), it forwards to index.jsp. More precisely, it forwards to /WEB-INF/view/index.jsp because VIEW_PREFIX parameter in web.xml is specified as /WEB-INF/view.
Now, let's see webapp/WEB-INF/struts-config.xml. You don't see any configurations about actions nor actionforms. SAStruts seeks appropriate action classes for the URL, picks up information from them, then reconstructs required configurations equivalent to struts-config.xml.
Where is webapp/WEB-INF/validation.xml ? You don't see such a file. SAStruts reconstructs required configurations equivalent to validation.xml from annotations added to properties for validation.
Add numbers
Access Add Numbers page from the index page. [NOTE: Currently, the index page is written only in Japanese. You may try an alternative way mentioned in the next sentence :] Or, open http://localhost:8080/sa-struts-tutorial/add with your browser. It's the same thing. You see Add Numbers page.
The action class corresponding to /add is tutorial.action.AddAction . For details of actions, please see here.
Source code for AddAction.java is as follows :
AddAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.IntegerType; import org.seasar.struts.annotation.Required; public class AddAction { @Required @IntegerType public String arg1; @Required @IntegerType public String arg2; public Integer result; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(input = "index.jsp") public String submit() { result = Integer.valueOf(arg1) + Integer.valueOf(arg2); return "index.jsp"; } }
An action is a POJO (an ordinary Java class with no specific superclass). The statuses of the action are also defined in the action. That's because it is easier to understand if you find related multiple information in one class.
As Seasar2 can recognize public fields as properties, it is not necessary to write getters nor setters. In SAStruts, public fields are recognizable even from EL and Struts. If you are interested in the mechanism, read the source code in org.seasar.struts.action package.
Request handling should be written in execution methods. Every execution method have an @Execute annotation. Its return value is the path of the page to forward to.
SAStruts uses the URL and optionally a request parameter to determine which of multiple execution methods to call.
In the following example, AddAction#index() is called. The string after /add will be the method name.
http://localhost:8080/sa-struts-tutorial/add/index
If no methods are defined, index() is called. Therefore, the invocation above is the same thing as below :
http://localhost:8080/sa-struts-tutorial/add/
In the case of form submit, specify the name of the execution method to name attribute of the button. In the following example, submit() method of the action class is called.
<input type="submit" name="submit" value="Submit this!"/>
For details of @Execute, please see here.
To validate with annotations (validators), add annotations for validation to the fields and set validator attribute of @Execute to true (this is default). When validator=true, you have to specify a forward page on validation failure with input attibute. For details of validators, please see here.
/add/index.jsp is as this :
/add/index.jsp
<html> <head> <title>Add</title> </head> <body> <html:errors/> <s:form> <html:text property="arg1"/> + <html:text property="arg2"/> = ${f:h(result)}<br /> <input type="submit" name="submit" value="Submit This!"/> </s:form> </body> </html>
The declaration for taglib used commonly in every JSP is defined in webapp/WEB-INF/common/common.jsp. For details of common.jsp, see JSP.
To show messages on validation error, put html:errors tag.
To receive the submitted values, put s:form. Normally, you don't have to specify action attribute because it is automatically determined.
Put html:text tags to receive input values. Specify the property names in the action.
Set name attribute of submit tag to "submit" in order to call AddAction#submit() on submission.
Once submitted, the input values arg1 and arg2 are set to the action and checked if they are present and both of them are integer, according to the specified validation rules, i.e. @Required and @IntegerType.
If the validations fail, SAStruts will forward control back to index.jsp specified by input attribute. If the validations are performed successfully, submit() will be invoked.
Then it forwards to index.jsp again because the return value of submit() is index.jsp.
result property of the action is set as an attribute of the request with the same name ("result") , and it can be displayed with ${f:h(result)}. f:h is a function to escape HTML. For details, please see here.
File Upload
Access File Upload page from the index page. [NOTE: Currently, the index page is written only in Japanese. You may try an alternative way mentioned in the next sentence :] Or, open http://localhost:8080/sa-struts-tutorial/upload with your browser. It's the same thing. You see the File Upload page.
The action class corresponding to /upload is tutorial.action.UploadAction . For details of actions, please see here.
Source code for UploadAction.java is as follows:
UploadAction.java
package tutorial.action; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.apache.struts.upload.FormFile; import org.seasar.framework.container.annotation.tiger.Binding; import org.seasar.framework.container.annotation.tiger.BindingType; import org.seasar.framework.exception.IORuntimeException; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.Required; import org.seasar.struts.upload.S2MultipartRequestHandler; import org.seasar.struts.util.ActionMessagesUtil; public class UploadAction { @Required @Binding(bindingType = BindingType.NONE) public FormFile formFile; @Binding(bindingType = BindingType.NONE) public FormFile[] formFiles; public HttpServletRequest request; public ServletContext application; @Execute(validator = false) public String index() { SizeLimitExceededException e = (SizeLimitExceededException) request .getAttribute(S2MultipartRequestHandler.SIZE_EXCEPTION_KEY); if (e != null) { ActionMessages errors = new ActionMessages(); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "errors.upload.size", new Object[] { e.getPermittedSize(), e.getActualSize() })); ActionMessagesUtil.addErrors(request, errors); } return "index.jsp"; } @Execute(input = "index.jsp") public String upload() { ActionMessages messages = new ActionMessages(); upload(formFile, messages); for (FormFile file : formFiles) { upload(file, messages); } ActionMessagesUtil.addMessages(request, messages); return "index.jsp"; } protected void upload(FormFile file, ActionMessages messages) { if (file.getFileSize() == 0) { return; } String path = application.getRealPath("/WEB-INF/work/" + file.getFileName()); try { OutputStream out = new BufferedOutputStream(new FileOutputStream( path)); try { out.write(file.getFileData(), 0, file.getFileSize()); } finally { out.close(); } } catch (IOException e) { throw new IORuntimeException(e); } messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "messages.upload.complete", new Object[] { path })); } }
When you want to add file upload feature to your action, you must define one or more properties of type FormFile to your Action or ActionForm and access them to obtain the uploaded files.
Since FormFile is an interface, a property of that type is the target of automatic binding by Seasar2, despite it should not be and a "skip setting property" warning occurs on runtime. To avoid that, a FormFile type property must have @Binding(bindingType = BindingType.NONE) annotation.
Define the property type with array style, i.e. FormFile[], then it can receive multiple FormFile.
You cannot specify an annotation for validation to the FormFile array. So, for each element of the array, check that FormFile#getFileSize() returns a value greater than zero to judge if it is really uploaded.
If the size of the uploaded file exceeds the limit predetermined (by maxFileSize attribute of controller tag in struts-config.xml), then SizeLimitExceededException will be thrown.
In the case that exception is thrown, control will be forwarded to index() method. You can check if the upload size has exceeded the limit by checking the presence of SizeLimitExceededException [ as an attribute of the request; see the example code above ].
/upload/index.jsp is as this :
/upload/index.jsp
<html> <head> <title>Upload</title> </head> <body> <html:errors/> <html:messages id="m" message="true"> ${f:h(m)}<br /> </html:messages> <s:form action="/upload" enctype="multipart/form-data"> <input type="file" name="formFile" /><br /> <c:forEach varStatus="s" begin="0" end="1"> <input type="file" name="formFiles[${s.index}]" /><br /> </c:forEach> <input type="submit" name="upload" value="Upload this!"/> </s:form> </body> </html>
For file upload, set enctype attribute of form to "multipart/form-data". Set the names of the properties of Action or ActionForm to name attributes of the tags for upload (<input type="file" .../>). For FormFile array, set the value of name attribute to property_name[index].
Client-side Validator
Access Client-Side Validator page from the index page. [NOTE: Currently, the index page is written only in Japanese. You may try an alternative way mentioned in the next sentence: ] Or, open http://localhost:8080/sa-struts-tutorial/clientValidator/ with your browser. It's the same thing. You see the Client-Side Validator page.
Don't enter anything and click "aaa is required" button. [NOTE: Currently, the button is in Japanese, but you may find which button to click, with "aaa" letter sequence.] JavaScript validation will be invoked and a message "aaa is required. " will be shown.
Then similarly, click "bbb is required" button. JavaScript validation will be invoked and a message "bbb is required. " will be shown. That shows that different validations are perfomed for each button.
The action class corresponding to /clientValidator is tutorial.action.ClientValidatorAction. For details of actions, please see here.
Source code for ClientValidatorAction.java is as follows:
ClientValidatorAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.Required; public class ClientValidatorAction { @Required(target = "submit") public String aaa; @Required(target = "submit2") public String bbb; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(validator = true, input = "index.jsp") public String submit() { return "index.jsp"; } @Execute(validator = true, input = "index.jsp") public String submit2() { return "clientValidator.jsp"; } }
If you want to use different validator for each button (method), you have to specify the names of the methods to target attribute to the annotations for validators. To specify multiple methods, separate with comma(,) .
/clientValidator/index.jsp is as this :
/clientValidator/index.jsp
<html> <head> <title>Client Validator</title> <html:javascript formName="clientValidatorActionForm_submit"/> <html:javascript formName="clientValidatorActionForm_submit2"/> </head> <body> <html:errors/> <s:form action="/clientValidator"> aaa:<html:text property="aaa"/><br /> bbb:<html:text property="bbb"/><br /> <input type="submit" name="submit" value="aaa is required" onclick="forms[0].name='clientValidatorActionForm_submit'; return validateClientValidatorActionForm_submit(forms[0]);"/> <input type="submit" name="submit2" value="bbb is required" onclick="forms[0].name='clientValidatorActionForm_submit2'; return validateClientValidatorActionForm_submit2(forms[0]);"/> </s:form> </body> </html>
To render JavaScript for validation, use html:javascript tag. Specify "action name + `Form' + `_' + method name" to formName attribute.
To invoke validator JavaScript, specify to onclick attribute of the button's tag a code fragment which first assigns the above formName attribute value to name attribute of form element, then calls the method named "validate + formName attribute value".