Struts 1
Struts 1
Struts 1
4 Action Classes
The Action class defines two methods that could be executed depending on your servlet environment:
public ActionForward execute(ActionMapping mapping, ActionForm form, ServletRequest request, ServletResponse response) throws Exception; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception;
Since the majority of teams using the framework are focused on building web applications, most projects will only use the "HttpServletRequest" version. A non-HTTP execute() method has been provided for applications that are not specifically geared towards the HTTP protocol.
The goal of an Action class is to process a request, via its execute method, and return an ActionForward object that identifies where control should be forwarded (e.g. a JSP, Tile definition, Velocity template, or another Action) to provide the appropriate response. In the MVC/Model 2 design pattern, a typical Action class will often implement logic like the following in its execute method:
Validate the current state of the user's session (for example, checking that the user has successfully logged on). If the Action class finds that no logon exists, the request can be forwarded to the presentation page that displays the username and password prompts for logging on. This could occur because a user tried to enter an application "in the middle" (say, from a bookmark), or because the session has timed out, and the servlet container created a new one. If validation is not complete, validate the form bean properties as needed. If a problem is found, store the appropriate error message keys as a request attribute, and forward control back to the input form so that the errors can be corrected. Perform the processing required to deal with this request (such as saving a row into a database). This can be done by logic code embedded within the Action class itself, but should generally be performed by calling an appropriate method of a business logic bean. Update the server-side objects that will be used to create the next page of the user interface (typically request scope or session scope beans, depending on how long you need to keep these items available). Return an appropriate ActionForward object that identifies the presentation page to be used to generate this response, based on the newly updated beans. Typically, you will acquire a reference to such an object by calling findForward on either the ActionMapping object you received (if you are using a logical name local to this mapping), or on the controller servlet itself (if you are using a logical name global to the application).
org.apache.struts.actions
Class DispatchAction
java.lang.Object org.apache.struts.action.Action org.apache.struts.actions.BaseAction org.apache.struts.actions.DispatchAction
An abstract Action that dispatches to a public method that is named by the request parameter whose name is specified by the parameter property of the corresponding ActionMapping. This Action is useful for developers who prefer to combine many similar actions into a single Action class, in order to simplify their application design. To configure the use of this action in your struts-config.xml file, create an entry like this:
<action path="/saveSubscription" type="org.apache.struts.actions.DispatchAction" name="subscriptionForm" scope="request" input="/subscription.jsp" parameter="method"/>
which will use the value of the request parameter named "method" to pick the appropriate "execute" method, which must have the same signature (other than method name) of the standard Action.execute method. For example, you might have the following three methods in the same action:
public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception public ActionForward insert(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception public ActionForward update(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception
Display modes
Struts-Layout 0.4 introduces the notion of form and field display modes. To explain their use, let's imagine you are running an online shop selling books. There is one jsp to display information about the book (title, author, size, price etc.). The customers can only view the information, but the site administrator can view and modify the information. Normally, you would have to make two jsps to achieve this: one to display the information, and one to modify it (you can also make one jsp with the complicated logic stuff). With Struts-Layout, it is possible to make only one jsp, without logic in it. The goal is : One user object to create / edit / view, one DistpachAction class, one Form class, and one jsp.
The form display mode is used to specify if a form in a jsp is displayed to view information (inspection mode) or to modify it (edit mode).
The form display mode is set in the Struts action by calling the FormUtils.setFormDisplayMode method. For example, FormUtils.setFormDisplayMode(request, form, FormUtils.INSPECT_MODE) will set the form mode to EDIT. Struts-Layout also support a creation display mode, and will support user defined display mode.
The field display mode is used to specify if an input field should be writeable, readonly, hidden, or totally skipped in a given form display mode.
This allows a field whose value is a primary key to be editable only during the creation, or fields whose values are computed automatically to be displayed only when viewing the information, and not when modifying it. The default behavior for an input field is to display itself as writeable (typical form field) if the form display mode is CREATE or EDIT, and to display itself readonly (the field value in plain text, and in an hidden input field) if the form display mode is INSPECT. This can be altered by using the attribute "mode" of the different Struts-Layout input field tags. The value of this attribute must respect the following simple pattern: X,Y,Z where X is the behaviour in creation mode, Y in edit mode, and Z in inspection mode. Valid behaviours are
E: editable (read-write) I: inspect, with an hidden input field (read-only) S: show, as I but without hidden field N: not displayed at all P: present (as S if the field value is not null, as N if the field value is null) H: hidden (not displayed, but a hidden input field is generated) R : readonly (an input field is generated with the readonly attribute set) D : disabled (an input field is generated with the disabled attribute set)
Like a form, the field display mode may be set in the Struts action by calling the FormUtils.setFieldDisplayMode method. For example, FormUtils.setFieldDisplayMode(request, form, fieldName, AbstractModeFieldTag.EDIT_INSPECT) will set the field mode to EDIT.
Important note: The <layout:field> tag can not be used with form and field display modes. The <layout:text>, <layout:password>, <layout:textarea>, <layout:file> tags must be use instead. Here is the code of a page using those attributes. There are no logic tags in it. A different display mode is set in the Struts action when the form is submited.
<%@taglib uri="/WEB-INF/struts-layout.tld" prefix="layout" %> <html> <head> <layout:skin/> </head> <body> <layout:form align="center" action="/registrationExample.do" reqCode="create" styleClass="FORM" width="50%"> <layout:text key="prompt.username" property="username" styleClass="LABEL" mode="E,I,N"/> <layout:password key="prompt.password" property="password" styleClass="LABEL" mode="E,I,N"/> <layout:select key="prompt.password2" property="password2" styleClass="LABEL" mode="E,I,N"> <layout:options name="usernames"/> </layout:select> <layout:radios key="prompt.fullName" property="fullName" styleClass="LABEL" mode="E,I,N"> <layout:options name="usernames"/> </layout:radios> <layout:checkboxes key="prompt.fullName" property="array" styleClass="LABEL" mode="E,I,N"> <layout:options name="greetings"/> </layout:checkboxes> <layout:formActions> <layout:submit mode="N,D,D" reqCode="testCreate">Edit</layout:submit> <layout:submit mode="D,N,D" reqCode="testEdit">Inspect</layout:submit> <layout:submit mode="D,D,N" reqCode="testInspect">Not displayed</layout:submit> </layout:formActions> </layout:form> </body> </html>
Result with the fields in edit mode Result with the fields in inspect mode Result with the fields in not displayed mode Now here is a very simple application, which allows the user to list, modifiy and create user registrations. There is only one jsp, and one dispatch action. The dispatch action has 3 display methods; inspect (choose and display registrations), edit (modify a registration) and create (create a registration) and 2 save method (save and saveNew)
which is difficult to type correctly, confuses HTML developers who are not knowledgeable about programming concepts, and can cause problems with HTML editors. Instead, Struts Taglibs provides a comprehensive facility for building forms, based on the Custom Tag Library facility of JSP 1.1. The case above would be rendered like this using Struts Taglibs:
<html:text property="username"/>;
with no need to explicitly refer to the JavaBean from which the initial value is retrieved. That is handled automatically by the JSP tag, using facilities provided by the framework. HTML forms are sometimes used to upload other files. Most browsers support this through a <input type="file"> element, that generates a file browse button, but it's up to the developer to handle the incoming files. The framework handles these "multipart" forms in a way identical to building normal forms.
The validate method is called by the controller servlet after the bean properties have been populated, but before the corresponding action class's execute method is invoked. The validate method has the following options:
Perform the appropriate validations and find no problems -- Return either null or a zerolength ActionErrors instance, and the controller servlet will proceed to call the execute method of the appropriate Action class. Perform the appropriate validations and find problems -- Return an ActionErrors instance containing ActionMessage's, which are classes that contain the error message keys (into
the application's MessageResources bundle) that should be displayed. The controller servlet will store this array as a request attribute suitable for use by the <html:errors> tag, and will forward control back to the input form (identified by the input property for this ActionMapping ). As mentioned earlier, this feature is entirely optional. The default implementation of the validate method returns null, and the controller servlet will assume that any required validation is done by the action class. One common approach is to perform simple, prima facia validations using the ActionForm validate method, and then handle the "business logic" validation from the Action. The Struts Validator, covered in the next section, may be used to easily validate ActionForms. Esempio1 Controllo effettuato nella classe che estende DispatchAction:
public ActionForward inserisci(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DistruzioneForm formBean = (DistruzioneForm) form; if(Utility.checkNull(formBean.getDataInizio()) == null){ errors.add("dataInizio", new ActionMessage("error.comuni.campoObbligatorio")); saveErrors(request, errors); return mapping.getInputForward(); } // errors di tipo ActionMessages // il metodo saveErrors() Save the specified error messages keys into the
//appropriate request attribute for use by the <html:errors> tag, if any messages are //required.
// altro codice
Nella jsp se si utilizzano tag appartenenti alla taglib Layout di Struts 1 (tag es. field, text, ) => The tags display the errors which are associated with their properties (NOTA: non inserire in essi la propriet layout = false).
The TilesPlugin configures a special RequestProcessor that determines if the requested view is a tile and processes it accordingly. Note that we made the homepage tile extend our root layout tile and changed the body attribute. Tiles inserts the file named in the body attribute into the main layout.
process user requests, determine what the user is trying to achieve according to the request, pull data from the model (if necessary) to be given to the appropriate view, and select the proper view to respond to the user.
Our controller delegates most of this grunt work to the Request Processor and Action classes. In addition to being the front controller for your application, the ActionServlet instance also is responsible for initialization and clean-up of resources. When the controller initializes, it first loads the application config corresponding to the "config" init-param. It then goes through an enumeration of all init-param elements, looking for those elements who's name starts with config/. For each of these elements, the framework loads the configuration file specified by the value of that init-param, and assigns a "prefix" value to that module's ModuleConfig instance consisting of the piece of the init-param name following "config/". For example, the module prefix specified by the init-param config/foo would be "foo". This is important to know, since this is how the controller determines which module will be given control of processing the request. To access the module foo, you would use a URL like:
http://localhost:8080/myApp/foo/someAction.do
For each request made of the controller, the method process(HttpServletRequest, HttpServletResponse) will be called. This method simply determines which module should service the request and then invokes that module's RequestProcessor's process method, passing the same request and response.
The ActionForm class itself requires no specific methods to be implemented. It is used to identify the role these particular beans play in the overall architecture. Typically, an ActionForm bean will have only property getter and property setter methods, with no business logic. The ActionForm object also offers a standard validation mechanism. If you override a "stub" method, and provide error messages in the standard application resource, The framework will automatically validate the input from the form (using your method). See Automatic Form Validation for details. Of course, you can also ignore the ActionForm validation and provide your own in the Action object. Define a property (with associated getXxx and setXxx methods) for each field that is present in the form. The field name and property name must match according to the usual JavaBeans conventions (see the Javadoc for the java.beans.Introspector class for a start on information about this). For example, an input field named username will cause the setUsername method to be called. Buttons and other controls on your form can also be defined as properties. This can help determine which button or control was selected when the form was submitted. Remember, the ActionForm is meant to represent your data-entry form, not just the data beans. Think of your ActionForm beans as a firewall between HTTP and the Action. Use the validate method to ensure all required properties are present, and that they contain reasonable values. An ActionForm that fails validation will not even be presented to the Action for handling. You may also place a bean instance on your form, and use nested property references. For example, you might have a "customer" bean on your ActionForm, and then refer to the property "customer.name" in your presentation page. This would correspond to the methods customer.getName() and customer.setName(String Name) on your customer bean. See the Apache Struts Taglib Developer Guides for more about using the nested syntax. Caution: If you nest an existing bean instance on your form, think about the properties it exposes. Any public property on an ActionForm that accepts a single String value can be set with a query string. It may be useful to place beans that can affect the business state inside a thin "wrapper" that exposes only the properties required. This wrapper can also provide a filter to be sure runtime properties are not set to inappropriate values.
Write code for a multi-threaded environment - Our controller servlet creates only one instance of your Action class, and uses this one instance to service all requests. Thus, you need to write thread-safe Action classes. Follow the same guidelines you would use to write thread-safe Servlets. Here are two general guidelines that will help you write scalable, thread-safe Action classes: o Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class. Local variables are created on a stack that is assigned (by your JVM) to each request thread, so there is no need to worry about sharing them. An Action can be factored into several local methods, so long as all variables needed are passed as method parameters. This assures thread safety, as the JVM handles such variables internally using the call stack which is associated with a single Thread. o Conserve Resources - As a general rule, allocating scarce resources and keeping them across requests from the same user (in the user's session) can cause scalability problems. For example, if your application uses JDBC and you allocate a separate JDBC connection for every user, you are probably going to run in some scalability issues when your site suddenly shows up on Slashdot. You should strive to use pools and release resources (such as database connections) prior to forwarding control to the appropriate View component -- even if a bean method you have called throws an exception. Don't throw it, catch it! - Ever used a commercial website only to have a stack trace or exception thrown in your face after you've already typed in your credit card number and clicked the purchase button? Let's just say it doesn't inspire confidence. Now is your chance to deal with these application errors - in the Action class. If your application specific code throws expections you should catch these exceptions in your Action class, log them in your application's log (servlet.log("Error message", exception)) and return the appropriate ActionForward.
It is wise to avoid creating lengthy and complex Action classes. If you start to embed too much logic in the Action class itself, you will begin to find the Action class hard to understand, maintain, and impossible to reuse. Rather than creating overly complex Action classes, it is generally a good practice to move most of the persistence, and "business logic" to a separate application layer. When an Action class becomes lengthy and procedural, it may be a good time to refactor your application architecture and move some of this logic to another conceptual layer; otherwise, you may be left with an inflexible application which can only be accessed in a web-application environment. The framework should be viewed as simply the foundation for implementing MVC in your applications. Struts provides a useful control layer, but it is not a fully featured platform for building MVC applications, soup to nuts.
type - Fully qualified Java class name of the Action implementation class used by this mapping. name - The name of the form bean defined in the config file that this action will use. path - The request URI path that is matched to select this mapping. See below for examples of how matching works and how to use wildcards to match multiple request URIs. unknown - Set to true if this action should be configured as the default for this application, to handle all requests not handled by another action. Only one action can be defined as a default within a single application. validate - Set to true if the validate method of the action associated with this mapping should be called. forward - The request URI path to which control is passed when this mapping is invoked. This is an alternative to declaring a type property.
<form-beans> This section contains your form bean definitions. Form beans are descriptors that are used to create ActionForm instances at runtime. You use a <form-bean> element for each form bean, which has the following important attributes:
name: A unique identifier for this bean, which will be used to reference it in corresponding action mappings. Usually, this is also the name of the request or session attribute under which this form bean will be stored. type: The fully-qualified Java classname of the ActionForm subclass to use with this form bean.
<global-forwards> This section contains your global forward definitions. Forwards are instances of the ActionForward class returned from an Action's execute method. These map logical names to specific resources (typically JSPs), allowing you to change the resource without changing references to it throughout your application. You use a <forward> element for each forward definition, which has the following important attributes:
name: The logical name for this forward. This is used in your Action's execute method to forward to the next appropriate resource. Example: homepage path: The context relative path to the resource. Example: /index.jsp or /index.do redirect: True or false (default). Should the ActionServlet redirect to the resource instead of forward?
<action-mappings> This section contains your action definitions. You use an <action> element for each of the mappings you would like to define. Most action elements will define at least the following attributes:
path: The application context-relative path to the action. type: The fully qualified java classname of your Action class. name: The name of your <form-bean> element to use with this action
parameter: A general-purpose attribute often used by "standard" Actions to pass a required property. roles: A comma-delimited list of the user security roles that can access this mapping.
For a complete description of the elements that can be used with the action element, see the configuration DTD or the online DTDDoc docs, and the ActionMapping documentation.
First the form bean is defined. A basic bean of class "org.apache.struts.webapp.example.LogonForm" is mapped to the logical name "logonForm". This name is used as a request attribute name for the form bean.
The "global-forwards" section is used to create logical name mappings for commonly used presentation pages. Each of these forwards is available through a call to your action mapping instance, i.e. mapping.findForward("logicalName"). As you can see, this mapping matches the path /logon (actually, because the MailReader example application uses extension mapping, the request URI you specify in a JSP page would end in /logon.do). When a request that matches this path is received, an instance of the LogonAction class will be created (the first time only) and used. The controller servlet will look for a bean in request scope under key logonForm, creating and saving a bean of the specified class if needed. Optional but very useful are the local "forward" elements. In the MailReader example application, many actions include a local "success" and/or "failure" forward as part of an action mapping.
<!-- Edit mail subscription --> <action path="/editSubscription" type="org.apache.struts.webapp.example.EditSubscriptionAction" name="subscriptionForm" scope="request" validate="false"> <forward name="failure" path="/mainMenu.jsp"/> <forward name="success" path="/subscription.jsp"/> </action>
Using just these two extra properties, the Action classes are almost totally independent of the actual names of the presentation pages. The pages can be renamed (for example) during a redesign, with negligible impact on the Action classes themselves. If the names of the "next" pages were hard coded into the Action classes, all of these classes would also need to be modified. Of course, you can define whatever local forward properties makes sense for your own application. The configuration file includes several other elements that you can use to customize your application. See Configuring Applications for details.
The initialization parameters supported by the action servlet are described below. (You can also find these details in the Javadocs for the ActionServlet class.) Square brackets describe the default values that are assumed if you do not provide a value for that initialization parameter.
chainConfig - Comma-separated list of either context-relative or classloader path(s) to load commons-chain catalog definitions from. If none specified, the default catalog that is provided with the framework will be used. (Since version 1.3) config - Context-relative path to the XML resource containing the configuration information for the default module. This may also be a comma-delimited list of configuration files. Each file is loaded in turn, and its objects are appended to the internal data structure. [/WEB-INF/struts-config.xml]. WARNING - If you define an object of the same name in more than one configuration file, the last one loaded quietly wins. WARNING - Plugins are not designed to be loaded more than once in the same module. The set of configuration files for a module should load a plugin only once. config/${module} - Context-relative path to the XML resource containing the configuration information for the application module that will use the specified prefix (/${module}). This can be repeated as many times as required for multiple application modules. (Since version 1.1) configFactory - The Java class name of the ModuleConfigFactory used to create the implementation of the ModuleConfig interface. (Since version 1.3) [org.apache.struts.config.impl.DefaultModuleConfigFactory] convertNull - Force simulation of the version 1.0 behavior when populating forms. If set to "true", the numeric Java wrapper class types (like java.lang.Integer ) will default to null (rather than 0). (Since version 1.1) [false] rulesets - Comma-delimited list of fully qualified classnames of additional org.apache.commons.digester.RuleSet instances that should be added to the Digester that will be processing struts-config.xml files. By default, only the RuleSet for the standard configuration elements is loaded. (Since version 1.1)
validating - Should we use a validating XML parser to process the configuration file (strongly recommended)? [true]
WARNING - The framework will not operate correctly if you define more than one <servlet> element for a controller servlet, or a subclass of the standard controller servlet class. The controller servlet MUST be a web application wide singleton.
which means that a request URI to match the /logon path described earlier might look like this:
http://www.mycompany.com/myapplication/do/logon
where /myapplication is the context path under which your application is deployed. Extension mapping, on the other hand, matches request URIs to the action servlet based on the fact that the URI ends with a period followed by a defined set of characters. For example, the JSP processing servlet is mapped to the *.jsp pattern so that it is called to process every JSP page that is requested. To use the *.do extension (which implies "do something"), the mapping entry would look like this:
<servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
and a request URI to match the /logon path described earlier might look like this:
http://www.mycompany.com/myapplication/logon.do
WARNING - The framework will not operate correctly if you define more than one <servletmapping> element for the controller servlet. WARNING - If you are using the new module support since version 1.1, you should be aware that only extension mapping is supported.
Note that you must use the full uri defined in the various tlds (see the example configuration for reference) so that the container knows where to find the tag's class files. You don't have to alter your web.xml file or copy tlds into any application directories. Of course, the configuration techniques use for older containers do still work.