Page 4 of 4
Data transfer object implementation
Problem
Usually a data transfer object (DTO) is used for shuttling data between the Web and EJB tiers. It is not a good idea to pass the view-helper class (form bean, in the case of Struts), to the EJB tier, primarily because all of the form bean's fields are of type string. A separate class should be used as the data transfer object. The problem is how to transfer the data from the form bean to the DTO.
Struts best practice
Two options are available for populating the data transfer object:
Create the transfer object and copy the data from the form bean to the transfer object. Data-type conversions must be handled before data copying. Similarly, on the way back, you must repeat the same exercise of copying the data from the DTO to the form bean.
Use Commons' BeanUtils class, which uses the Reflection API to achieve its objectives.
Developers frequently use the first option, which is not problematic except that the mundane exercise is repeated every time, resulting in bulky and ugly code. While copying data, suitable data-type conversion must be hand-coded in the DTO's setter methods, so that when data is copied from the form bean to the DTO, it converts to the appropriate business type. All the attributes of type string in the form beans are the primary reasons for data-type conversions.
You can avoid all this coding by using BeanUtils's copyProperties() method. The method copies the data from one bean to another, provided the variable names of the business attributes in the value/transfer object are the same as the ones in the form bean. The method also relieves you from data-type conversion by transparently converting source-bean attribute types to destination-bean attribute types.
Exceptions
Problem
As a generic principle, errors should be caught right where they occur to display meaningful error messages. To implement this principle, programmers must write their own custom exception classes, wrap the actual exception into these custom classes, and throw them back to the place where they are handled. In a Struts-based application, the exceptions thrown from the EJB/Web tier are handled either in the Action class or the form bean. The usual practice involves writing a try-catch block to catch these exceptions in the Action class or the form bean, and creating an ActionError object with user-friendly messages derived from the application property file. The decision to direct these error messages to the screen is coded in the action classes, using the ActionMapping.findForward() method.
You must code all this exception-handling code in the Action class as well as the decision to direct the message to the screen. If the error-handling strategy changes, then every Action or form bean requires changes.
Struts best practice
Struts deals with the issue of exception handling in a competent way, using declarative exception handling. This, as opposed to programmatic exception handling (as explained above) handles issues using the RequestProcessor class's processException() method and a Struts configuration file. To use declarative exception handling, you must do the following:
Create custom application exception classes. You can design them so they hold more than one exception
In the EJB/Web tier, trap application errors and wrap them into a custom exception class and throw them back
In the struts-config.xml file, add the localized exception against an action-mapping or define a global exception (as shown below):
<struts-config>
<action-mappings>
<action
path="yourAction"
type="your.package.yourAction"
input="input.jsp" >
<exception
key="your.error.property.key"
path="yourException.jsp"
type="your.application.custom.exception"
</action>
</action-mappings>
Action chaining
Problem
A clear strategy for the relationship between JSP pages and Action classes in an application should be defined; i.e., how many JSP pages should be associated with an Action class.
For clarity and easy maintenance, the strategy for large applications should be to have one-to-one mapping between JSP pages and Action classes. With such a guideline in place, a prospective problem of duplicated code in the Action classes could result. To avoid logic duplication, you need some way to call an Action class's method from another Action class. Moreover for criss-cross page flow, you often need action chaining, or, to summarize, an implementation of the Chain of Responsibility design pattern.
Struts best practice
Possible solutions follow below:
Let ActionForwards point to a fresh request in the same application to invoke the method of another Action class.
RequestProcessor maintains a HashMap that stores all the instances of Action classes for that module. Extend the RequestProcessor class and provide getter methods for this HashMap. Provide a utility method in the application's parent Action class, which can invoke the methods of other Action classes within the same module. This utility method will use the Reflection API to achieve this.
The second approach is the best practice for large applications because if some change is required in exception handling, no code change will be required.
Conclusion
Large applications built using the above recommendations have been benchmarked for excellent performance.
As indicated by the Struts roadmap (featured on the Struts Website), features and extensions developed on sourceforge.net and those commonly used by the Struts community are candidates for inclusion into Struts per se. All these are worth reading about before you make critical decisions about your application.