Best Practices

 < Day Day Up > 



At this point in the book you have a thorough understanding of how sophisticated a tool Struts really is. This section addresses a range of best practices that, when adhered to, will help you use Struts to its fullest.

i18n Best Practices

If you know the history of Struts, then you know it was built from the ground up to support i18n. If there is a way to do something in an un-i18n way, there is usually also an i18n way to do the same thing. For example, this is how you format a String with bean:write:

 <bean:write name="employee" property="salary" format="$#.##"/> 

This code is great-unless of course you are supporting more than one locale, and one of the locales does not support the $ sign. A better way to do that would be:

 <bean:write name="employee" property="salary" formatKey="patterns.Currency"/> 

Now instead of hard-coding the way we do formatting, we grab the format key out of the i18n-enabled resource bundle.

Every time you can use a static string with the html:* tag library, you can also use a key to the resource bundle instead. For example:

 <html:image pageKey="icons.exit"   altKey="icons.exit.alternate" titleKey="icons.exit.title"/> 

The image tag has page, alt, and title attributes, but if you use those you mess up your i18n support. Always use the ${attributeName}Key version of the attribute instead. The previous code snippet grabs the alternative text from the image out of the resource bundle and also takes the title text out of the resource bundle. And (what may not appear to be as intuitive) it grabs the icon path (pageKey) out of the resource bundle. It does this because iconography is not the same from locale to locale. In addition, some images have text in them, which would vary from locale to locale.

In addition to the custom tags, both the Validator framework and the Tiles framework support i18n in their configuration files. Use this support liberally to vary things such as lists of links (Tiles) and different types of validation based on locale.

Read the DTDs

There are all kinds of golden nuggets and comments in the document type definition (DTD). You should have a copy of the source for whatever release you are using to develop your applications; the DTDs ship with the source. Read the DTDs for all of the configuration files (struts-config.xml, validation.xml, tiles-def.xml). It is time well spent.

Read the Source

You'll find all kinds of golden nuggets and comments in the source as well. You should have a copy of the source for whatever release you are using to develop your applications. If you want to develop a routine or custom tag, look for a custom tag or routine that does something similar in the Struts API, and then study how the creator implemented it. Also, when something does not work the way you think it should, you have the source (the ultimate documentation) to refer to. Keep the source handy. You will need it.

Understand MVC, Model 2, Sun's Blueprints for J2EE, and J2EE design patterns

To get the most out of Struts, you should have a background with Model-View-Controller (MVC) and Java 2 Enterprise Edition (J2EE) design patterns, such as the data transfer object (DTO), the front controller, and so forth. You should also understand Sun's vision for J2EE Web applications as explained in the Blueprint documentation. This includes a good understanding of MVC and Model 2 architecture. Also, get yourself a good book on J2EE design patterns. Struts will not prevent you from creating a large mess of an application. You need the requisite background in architecture and design of J2EE Web applications.

Don't Use Java Scriptlets in JSP (<% %>)

I hope that I am preaching to the choir. You should limit your Java scriplets to zilch. This does not apply to Tile layouts that are visual components (stand-ins for custom tags), but it does apply to Tile layouts that are site layouts. In my opinion, this does not include simple Java expressions in JSP (<%=%>).

I don't see much difference between

 <html:messages >   <p ><%= errorMessage %></p> </html:messages> 

and

 <html:messages >   <p ><bean:write name="errorMessage"/></p> </html:messages> 

except that the latter is more verbose. The purpose of getting rid of Java scriplets (<% %>) is to reduce the logic in your page, not for you to be more verbose. Be pragmatic, not dogmatic.

Limit the Logic in Your JSPs

I've seen some developers ban simple expressions (<%= errorMessage%>) yet litter their JSPs with Struts logic tags (logic:present, logic:empty, logic:notEmpty, logic:match, core:if, and so forth). They are rigid about the details but miss the principle behind the rule. The point is to limit your logic as much as possible in your JSP whether you implement that logic with JSP Standard Tag Library (JSTL) or Struts logic tags. If possible, try to limit your logic to iteration (logic:iterate and core:forEach regularly) and see if an object is present before displaying it.

There are several ways to limit the logic in your JSPs. You can use the logic:present tag to see whether model objects are mapped into scope. If they are, then you display them (otherwise, you do not display them). This way, you move the logic while it is in scope to the Controller (the action). If you change the rules and map them into scope for different reasons, the JSP never knows the difference. Keep the logic in your JSPs thin.

Another approach to limiting the logic in the page is to use Tiles and forward to Tile definitions. This is a powerful approach. You can use it to eliminate a lot of logic in your JSPs. For example, if a user is logged in as a manager versus a regular user, you can forward that user to a definition that specifies parameter Tiles that only a manager can use. The manager's definition can extend the regular uses definition and define attributes that include areas only for the manager. The two types of users can even use the same Tile layout if the layout uses the insert tag with the ignore attribute (to clarify areas that are unavailable if the person is logged in as a user but available in the manager's definition). The action could select the correct forward, and you would not have to use a logic:* tag at all. Getting logic out of your JSPs and into the Controller is a step in the right direction and is so much easier when you are using Tiles.

If you see yourself writing the same three custom tags over and over, either create a Tile layout that acts a visual component or write a custom tag. See the earlier section "Context-Sensitive Error Messages" and Chapter 13 for more details.

Create Multiple Resource Bundles per Module

You spent all of this time making your application object oriented, modular, and well designed. You've mastered MVC, and you've limited the logic in your JSPs. Your actions never talk directly to the database using Java Database Connectivity (JDBC); they always talk to the model to get DTOs and choose the next view. Yet you have a resource bundle that has 5000 messages!

Break up your resource bundle by configuring more than one bundle in your module. (Even if you are not using modules, you get a default module). You might have a bundle for just form labels, and another bundle that is just for patterns, and so forth. Remember that the bean:message tag takes a bundle parameter so that you can specify a bundle.

Break Up Your struts-config.xml File

Your struts-config.xml file is huge! Did you know that you can break it up without creating a new module? The config init-parameter of the Action servlet takes a comma-delimited list of files. This means that you can break up your struts-config.xml file into several smaller, more manageable files (e.g., struts-config.xml, struts-config-forms.xml). Perhaps you could group similar action mappings into one configuration file. Every configuration file in the comma-delimited list can be in the same module.

Break Your Application Up into Modules

You've noticed that you can break up your application into nearly stand-alone modules. Well, you can break up your application into modules. See Chapter 4, "Actions and the ActionServlet" for more details.

The nice thing about breaking your application up into modules is that each module can have its own configuration-for instance, its own RequestProcessor. Also, modules are nice if you have several teams working on the same site and you are tired of stepping on each other's toes. Divide and conquer!

Use html:messages instead of html:errors

The problem with html:errors is that you have to put markup in your resource bundle. Yuck! The nice thing about html:messages is that it iterates over the messages and allows you to leave the markup in the JSP where it belongs.

Remember that when you use html:errors you have to define the following in your resource bundle:

 errors.header=<ul> errors.footer</ul> errors.prefix=<li > errors.suffix=</li> 

Now you can do the same with html:messages in the JSP:

 <ul> <html:messages >   <li ><bean:write name="errorMessage" /></p> </html:messages> </ul> 

This helps you keep the i18n text in the resource bundle and the markup in the JSP page. By default, html:messages works with errors. If you want to work with message, set the message attribute to true. See section "Action saveMessages and getResources" earlier in this chapter for more details.

Develop Your Own Custom Tags

Learn how to develop custom tags. It will help you to create more maintainable JSPs. See the section "Context-Sensitive Error Messages" for a more in-depth discussion. The ability to write custom tags is definitely something you want to add to your repertoire. Many good books and resources are available that describe how to write custom tags, including Mastering JSP Custom Tags and Tag Libraries, also written by James Goodwill.

Declaratively Handle Exceptions

One of the major additions to Struts 1.1 is the ability to declaratively define exception handlers in the Struts configuration file. This is nice because it allows you to separate your go code from your exception-handling code. The go code is located in your actions, and the exception-handling code is located in exception handlers. The Struts Framework can handle "uncaught" application exceptions in two ways: by specifying global exception handlers and by specifying a local exception handler per action mapping (as follows):

 <action path="/addUser"         type="richhightower.UserAction"         input="/userForm.jsp" name="userForm"         scope="request" validate="true">   <exception key="exception.user.exists"              type="rickhightower.UserExistsException"              path="/exceptions/user_exists.jsp"/>   <forward name="success"            path="/userAdded.jsp"/> </action> 

You can use html:errors or html:messages in the user_exists.jsp page to display the error message corresponding to the message associated with the exception.user.exists key in the resource bundle. The previous code uses the default exception handler (org.apache.struts.action.ExceptionHandler). If you need an exception handler to take a certain action or perform a certain task, then you could override the exception handler and configure your own using the className attribute:

 <action path="/addUser"         type="richhightower.UserAction"         input="/userForm.jsp" name="userForm"         scope="request" validate="true">   <exception key="exception.user.exists"              type="rickhightower.UserExistsException"              path="/exceptions/user_exists.jsp"              className="rickhightower.CustomExceptionHandler"/>   <forward name="success"            path="/userAdded.jsp"/> </action> 

Here is an example of defining your own custom exception handler:

 public class CustomExceptionHandler extends ExceptionHandler {     public ActionForward execute(Exception exception,                                  ExceptionConfig econfig,                                  ActionMapping mapping,                                  ActionForm form,                                  HttpServletRequest request,                                  HttpServletResponse response)         throws ServletException {         if (exception instanceof rickhightower.UserExistsException){            //do something special            ...         }         return super.execute(exception, econfig, mapping,                              form, request, response);     } ... 

This code does something special with the exception and then delegates the rest of the flow to the basic exception handler (super class).

For Visual Components, Use Tile Layouts

If you have a visual component and it uses a lot of HTML markup, you should probably implement it as a Tile layout instead of a custom tag. It is okay to use Java scriptlets in this type of JSP. If the JSP is meant as a visual component-that is, a Tile layout-then you can use some Java scriptlets. Try to reduce the scriptlets as much as possible by using a Tile controller. It is not wise to use scriptlets with Tile layouts that are used as site layouts (page layouts). There is no hard-and-fast rule when a Tile layout is a visual component or a page layout. Strive to keep visual components in a directory separate from your other JSPs. See Chapter 13 for more details.

Start Using Tiles

If you are not using Tiles in your Struts projects, you are missing out. Learn how to use Tiles as soon as possible; it will help you separate your logic from your pages and help you reuse markup and custom tags that you would otherwise repeat over and over. See Chapter 13 to learn more.

Start Using the Validator Framework

If you are not using Validator framework in your Struts projects to validate code, start as soon as possible. The Validator framework allows you to reuse validation code on many forms. For example, suppose you have a user login form and a user registration form. Both of these forms have a field called userName, and the userName is validated the same way on both forms. By using the Validator framework, you can define the validation for userName in one place. See Chapter 12, "Working with the Validator," for more information.

Group Related Actions

Actions are often too small, and this ruins the cohesiveness of your application. You can group related actions using DispatchAction or LookupDispatchAction or by using the parameter and writing your own dispatch. Let's look at an example that uses the parameter attribute of the action mapping to implement your own dispatch.

First, we use the same action handler in two action mappings, as follows:

    <action-mappings>        <action            path="/input"            type="masteringStruts.InputAction"            parameter="loadAddForm">          <forward name="success" path="/input.jsp"/>        </action>          <action              path="/inputSubmit"              type="masteringStruts.InputAction"              name="inputForm"              scope="request"              validate="true"              input="/input.jsp">          <forward name="success" path="/success.jsp"/>          <forward name="resubmit" path="/resubmit.jsp"/>        </action>    </action-mappings> 

Then we use the parameter attribute as the pivot point to decide which method to invoke (each method is like an action):

 public class InputAction extends Action {     public ActionForward execute(         ActionMapping mapping,         ActionForm form,         HttpServletRequest request,         HttpServletResponse response)         throws Exception {         if ("loadAddForm".equals(mapping.getParameter())) {             return loadAddUserForm(mapping, form, request, response);         } else {             return add(mapping, form, request, response);         }     }     public ActionForward loadAddUserForm(         ActionMapping mapping,         ActionForm form,         HttpServletRequest request,         HttpServletResponse response)         throws Exception {     }     public ActionForward add(         ActionMapping mapping,         ActionForm form,         HttpServletRequest request,         HttpServletResponse response)         throws Exception {         ... } ... 

The execute method invokes the loadAddUserForm method if the parameter is set to loadAddForm; otherwise, it executes the add method. This is simple and straightforward, and unlike DispatchAction, it is completely hidden from the client in the confines of the Struts configuration file.

To learn more about DispatchAction and LookupDispatchAction, see Chapter 5, "Advanced Action Classes."

Never Link to a JSP

Never link directly to a JSP page from a JSP page. This breaks the MVC model. An Action (part of the Controller) should always be executed before a JSP displays. For a complete discussion of this topic, see Chapter 5 (read the section "Linking to JSP Directly: More than Just Bad Design").

Extend Actions with Action Chaining

Let's say that you have an action that is a lot like another action. In fact, it does everything the first action does along with a few other things. At first, you might consider subclassing it, but subclassing adds a lot of coupling from your new action to the old action. Instead, you can extend the action using action chaining, as shown here:

    <action-mappings>        <action             path="/old"             type="masteringStruts.OldAction"             >          <forward name="success" path="/success.jsp"/>        </action>        <action             path="/extendedOld"             type="masteringStruts.OldAction"             >          <forward name="success" path="/new.do"/>        </action>        <action             path="/new"             type="masteringStruts.NewAction"             >          <forward name="success" path="/success.jsp"/>        </action>    </action-mappings> 

The extendedOld action mapping does everything the old action mapping did along with everything the new action mapping does-and we did not have to subclass the old action. You can extend other actions in the same manner.

Don't Use DynaActionForms without the Validator Framework

Don't use DynaActionForms without the Validator framework; otherwise, you will have to handle all of your form validation in your actions (using saveErrors). You'd be circumventing the Struts validation mechanism and workflow for not much benefit. To see how to integrate DynaActionForms with the Validator framework, see Chapter 12.

Use JSTL instead of Struts-Equivalent Tags

Use JSTL in place of the built-in Struts tags whenever their functionality overlaps. JSTL consists of four tag libraries: core, format, xml, and sql.

The core tag library is similar to the Struts bean and logic taglibs in that it provides custom actions for managing data through scoped variables, as well as iteration and logic based on the page content. The core library also has some aspects of the Struts html library in that it provides tags for generating and operating on URLs for URL rewriting (for session management).

The format taglib has tags for formatting numbers and dates. It also provides support for i18n similar to Struts' bean:message custom tag in the bean taglib.

The xml taglib provides tags for manipulating XML via XPath and Extensible Stylesheet Language (XSL)-like support. The sql taglib defines actions for manipulating and updating relational databases.

You can define JSTL custom tag Attribute values using the JSTL expression language (EL). The EL provides identifiers, accessors, and operators. It allows you to retrieve and manipulate values in JSP scopes (session, request, page, application), headers, cookies, and parameters. It takes the place of many of the bean:* tags (bean:header, bean:parameter, bean:define, bean:cookie).

The EL syntax resembles a combination of JavaScript and XML Path Language, with an emphasis on looking up beans and their properties and performing operations on them.

Use core:out instead of bean:write

EL expressions are delimited using a dollar sign ($) and curly braces ({}) as follows:

 <core:out value="${employee.firstName}"/> 

The previous snippet prints out the firstName property of the employee bean. It is similar in purpose to the following scriptlet:

 <%  Employee employee (Employee) = request.getAttribute("employee");  out.println( employee.getFirstName()); %> 

Unlike with Struts' bean:write, you can use many expressions in the same string:

 <core:out value="Employee Record: ${employee.firstName} ${employee.lastName}"/> 

Compare this snippet with doing the same thing using bean:write:

 Employee Record: <bean:write name="employee" property="firstName"/> <bean:write name="employee" property="lastName"/> 

Use core:set instead of bean:define

The <core:set> custom tag defines beans similar to the struts custom tag bean:define. The following defines the bean companyName with the value Intel in session scope:

 <core:set var="companyName" scope="session" value="Intel"/> 

Think of how you would do the bean:define equivalent. Also remember that the value can be any JSTL expression. The JSTL expression does not have to be simple; they can be fairly complex.

The following defines a bean called sum in request scope by multiplying the numeric equivalent of the request parameters dailySalary and numberOfDays:

 <core:set var="sum"    value="${param['dailySalary'] * param['numberOfDay']}"    scope="request"/> 

The expression param['dailySalary'] is equivalent to request.getParameter("dailySalary").

The param object is a built-in JSTL object that refers to request parameters. Think of it as a HashMap of request parameters.

There are many other built-in objects. Many of them are the same as the implicit objects of JSP pages with the addition of the following map-like objects: pageScope, requestScope, sessionScope, applicationScope, param, header, and initParam.

The JSTL EL negates the reason behind many of the Struts logic tags.

Use JSTL core:if and core:forEach instead of Struts logic:*

You can use the core:forEach tag in place of logic:iterate. For example, use

 <core:forEach items="${results}" var="currentCD">     <core:out value="${currentCD.ID}"/> <br /> </core:forEach> 

instead of:

 <logic:iterate name="results"  scope="request">       <jsp:getProperty name="currentCD" property="ID"/> <br /> </logic:iterate> 

The core:if tag checks to see if an expression is true. It has the following syntax:

 <core:if     test="expression"     var="name"     scope="scope">           body content </core:if> 

When you use the power of JSTL EL, the core:if tag takes the place of bean:equal, bean:notEqual, bean:greaterEqual, bean:lessEqual, bean:greaterThan, bean:lessThan, bean:present, and so on.

Covering all of the capabilities of JSTL and the JSTL EL is beyond the scope of this book, but you've got a glimpse of why you should use JSTL. Some tags in Struts have no JSTL equivalent. But when an equivalent is available, you should almost always use the JSTL version.

Use JSTL EL in Your Custom Tags

You want to start developing custom tags that support JSTL EL. How can you do this? It is actually quite simple. The Jakarta Taglibs project ships with an API for the JSTL EL so that you can use the same Expression Parser that the reference implementation of JSTL uses; namely, the ExpressionEvaluatorManager (org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager).

The ExpressionEvaluatorManager is easy to use because it has only one method:

 public static Object evaluate(                                   String  attributeName,                                   String expression,                                   Class expectedType,                                   Tag tag,                                   PageContext pageContext) 

Here is an example how the core:if tag uses the ExpressionEvaluatorManager:

 Object r = ExpressionEvaluatorManager .evaluate("test", test, Boolean.class, this, pageContext); 

As you can see, this code is pretty straightforward. The attribute is called test, so we pass test as the attributeName. The expression is from the test attribute, so we pass the test property from the custom tag. The core:if tag expects a Boolean (true or false), so we specify Boolean.class as the expectedType. If we did not know the expected type, we could pass Object.class. We know that the EL has access to all of the implicit objects of JSP, so we pass it the pageContext.

In addition to providing EL support to custom tags, JSTL provides Tag classes that simplify the lifecycle of tags, which makes them easier to write.

The class ConditionalTagSupport (javax.servlet.jsp.jstl.core.ConditionalTagSupport) facilitates development of conditional tags-that is, tags like <core:if>. The body of this tag is a conditional executed based on one single overridable condition() method.

Thus, to use this class you just need to subclass and override one method. The condition method returns true or false, and it represents the condition that the subclass uses to drive behavior. You don't have to implement doStartTag, doEndTag, or doAfterBody-you just have to implement condition(). Whew! It makes developing tags like this so much easier.

Here is an example of a JSTL-style tag that uses ConditionalTagSupport (it is similar to the tag we defined earlier without the JSTL API):

 public class IfErrorTag extends ConditionalTagSupport {     private String property;     public boolean condition(){     try{         ActionErrors actionErrors = RequestUtils.getActionErrors(pageContext,Globals.ERROR_KEY);         if (actionErrors == null){             return false;         }         Iterator iter = actionErrors.get(this.getProperty());         if (iter==null) return false;         if (iter.hasNext()==false)return false;         return true;     }catch(Exception e){         throw new RuntimeException(e);     }     }     /** Getter for property property.      * @return Value of property property.      * @jsp.attribute required="false"      *                  rtexprvalue="false"      *                  description="The property attribute"      */     public String getProperty() {         return property;     }     /**      * @param string      */     public void setProperty(String string) {         property = string;     } } 

The condition() method of this tag checks to see if the property (specified by the property attribute) has any errors associated with it. The LoopTagSupport() method facilitates the development of iteration custom tags. LoopTagSupport defines default logic for iteration tags to subclass.

Most iteration tags behave in a similar way with regard to the looping functionality, so LoopTagSupport implements this behavior. That way, you don't have to reinvent the wheel. All you need to do is subclass LoopTagSupport and override the next() and hasNext() methods.

The hasNext() method returns true if there are more items. The next() method advances the iteration and returns the object at the current iteration. The beautiful thing is that you don't have to implement doStartTag, doEndTag, doAfterBody, and so forth-you only have to implement next() and hasNext().



 < Day Day Up > 



Professional Jakarta Struts
Professional Jakarta Struts (Programmer to Programmer)
ISBN: 0764544373
EAN: 2147483647
Year: 2003
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net