Set - 4

Question 1 :

When is the best time to validate input?

Answer :

This is an excellent question. Let's step back a second and think about a typical mid to large size application. If we start from the back end and work toward the view we have:
1) Database: Most modern databases are going to validate for required fields, duplicate records, security constraints, etc.
2) Business Logic: Here you are going to check for valid data relationships and things that make sense for the particular problem you are triing to solve.
... This is where struts comes into the picture, by now the system should be pretty well bulletproof. What we are going to do is make validation friendlier and informative. Rember it is OK to have duplicate validations...
3) ActionErrors validate(ActionMapping map, HttpServletRequest req) is where you can do your validation and feed back to the view, information required to correct any errors. validate is run after the form has been reset and after the ActionForm properties have been set from corresponding view based input. Also remember you can turn validation off with validate="false" in the action mapping in the struts-config.xml. This is done by returning an ActionErrors collection with messages from your ApplicationResources.properties file.
Here you have access to the request so you can see what kinds of action is being requested to fine tune your validations. The tag allows you to dump all errors on your page or a particular error associated with a particular property. The input attribute of the struts-config.xml action allows you to send validation errors to a particular jsp / html / tile page.
4) You can have the system perform low level validations and client side feedback using a ValidatorForm or its derivatives. This will generate javascript and give instant feedback to the user for simple data entry errors. You code your validations in the validator-rules.xml file. A working knowledge of regular expressions is necessary to use this feature effectively.


Question 2 :

How can I avoid validating a form before data is entered?

Answer :

The simplest way is to have two actions. The first one has the job of setting the form data, i.e. a blank registration screen. The second action in our writes the registration data to the database. Struts would take care of invoking the validation and returning the user to the correct screen if validation was not complete.
The EditRegistration action in the struts example application illustrates this:

< action path="/editRegistration">
type="org.apache.struts.webapp.example.EditRegistrationAction" attribute="registrationForm" scope="request" validate="false">
<forward name="success path=" registration.jsp"="">

When the /editRegistration action is invoked, a registrationForm is created and added to the request, but its validate method is not called. The default value of the validate attribute is true, so if you do not want an action to trigger form validation, you need to remember to add this attribute and set it to false.


Question 3 :

How can I create a wizard workflow?

Answer :

The basic idea is a series of actions with next, back, cancel and finish actions with a common bean. Using a LookupDispatchAction is reccomended as it fits the design pattern well and can be internationalized easily. Since the bean is shared, each choice made will add data to the wizards base of information. A sample of struts-config.xml follows:

< form-beans>
<form-bean name="MyWizard" type="forms.MyWizard" />
</form-beans>

<!-- the first screen of the wizard (next action only available) -->
<!-- no validation, since the finish action is not available -->
<actions>
<action path="/mywizard1" type="actions.MyWizard" name="MyWizard" validate="false" input="/WEB-INF/jsp/mywizard1.jsp">
<forward name="next" path="/WEB-INF/jsp/mywizard2.jsp" />
<forward name="cancel" path="/WEB-INF/jsp/mywizardcancel.jsp" />
</action>

<!-- the second screen of the wizard (back, next and finish) -->
<!-- since finish action is available, bean should validated, note validation should not necessarily validate if back action requested, youmight delay validation or do conditional validation -->
<action path="/mywizard2" type="actions.MyWizard" name="MyWizard" validate="true" input="/WEB-INF/jsp/mywizard2.jsp">
<forward name="back" path="/WEB-INF/jsp/mywizard1.jsp" />
<forward name="next" path="/WEB-INF/jsp/mywizard3.jsp" />
<forward name="finish" path="/WEB-INF/jsp/mywizarddone.jsp" />
<forward name="cancel" path="/WEB-INF/jsp/mywizardcancel.jsp" />
</action>

<!-- the last screen of the wizard (back, finish and cancel only) -->
<action path="/mywizard3" type="actions.MyWizard" name="MyWizard" validate="true" input="/WEB-INF/jsp/mywizard3.jsp">
<forward name="back" path="/WEB-INF/jsp/mywizard2.jsp" />
<forward name="finish" path="/WEB-INF/jsp/mywizarddone.jsp" />
<forward name="cancel" path="/WEB-INF/jsp/mywizardcancel.jsp" />
</action>

The pieces of the wizard are as follows:
forms.MyWizard.java - the form bean holding the information required
actions.MyWizard.java - the actions of the wizard, note the use of LookupDispatchAction allows for one action class with several methods. All the real work will be done in the 'finish' method.
mywizard[x].jsp - the data collection jsp's
mywizarddone.jsp - the 'success' page
mywizardcancel.jsp - the 'cancel' page


Question 4 :

What's the best way to deal with migrating a large application from Struts to JSF? Is there any tool support that can help?

Answer :

This is a complicated task depending on your Struts application. Because the two frameworks have different goals, there are some challenges. Migrate your response pages first. Keep the Struts controller and place and forward to JSF pages. Then you can configure Struts forwards to go through the Faces servlet. Consider looking at the Struts-Faces framework from Apache. See the framework chapter in JSF in Action.


Question 5 :

How can I 'chain' Actions?

Answer :

Chaining actions can be done by simply using the proper mapping in your forward entries in the struts-config.xml file. 
Assume you had the following two classes:

/* com/AAction.java */
...
public class AAction extends Action{
	public ActionForward
	execute(ActionMapping mapping,
	ActionForm form,
	HttpServletRequest request,
	HttpServletResponse response) throws
	Exception{
		// Do something
		return mapping.findForward("success");
	}
}

/* com/BAction.java */
...
public class BAction extends Action{
	public ActionForward
	execute(ActionMapping mapping,
	ActionForm form,
	HttpServletRequest request,
	HttpServletResponse response) throws
	Exception{
		// Do something else
		return mapping.findForward("success");
	}
}

Then you can chain together these two actions with the Struts configuration as shown in the following excerpt:
...

<action-mappings type="org.apache.struts.action.ActionMapping">
<action path="/A" type="com.AAction" validate="false">
<forward name="success" path="/B.do" />
</action>
<action path="/B" type="com.BAction" scope="session" validate="false">
<forward name="success" path="/result.jsp" />
</action>
</action-mappings>

Here we are assuming you are using a suffix-based (.do) servlet mapping, which is recommended since module support requires it. When you send your browser to the web application and name the action A.do (i.e. http://localhost:8080/app/A.do) it will execute AAction.execute(), which will then forward to the "success" mapping. 
This causes the execution of BAction.execute() since the entry for "success" in the configuration file uses the .do suffix. 
Of course it is also possible to chain actions programmatically, but the power and ease of being able to "reroute" your web application's structure using the XML configuration file is much easier to maintain. 
As a rule, chaining Actions is not recommended. If your business classes are properly factored, you should be able to call whatever methods you need from any Action, without splicing them together into a cybernetic Rube Goldberg device. 
If you must chain Actions, be aware of the following: calling the second Action from the first Action has the same effect as calling the second Action from scratch. If both of your Actions change the properties of a formbean, the changes made by the first Action will be lost because Struts calls the reset() method on the formbean when the second Action is called.