MELJUN CORTES Jedi course notes web programming-lesson8-advanced struts
Advanced Struts TopicsIn the previous chapter, we were able to tackle the basics of Struts. We have learnedhow to include the Struts framework in our application by configuring the providedActionServlet to handle our requests. Also, we have learned how to create instances ofAction classes that serve as action handlers for form submissions and other userrequests. We have seen how to create ActionForm classes that provide an easy way oftransferring data from our forms into the designated ActionHandlers. Finally, we haveseen how to use the included tag libraries to help tie in the HTML forms in our JSP pagesmore firmly into the framework.In this chapter, we will cover some advanced techniques and features of the Strutsframework. First, we will see how we can make use of DynaActionForms to minimize thenumber of classes we need to do for the framework. Next, well look at how the Validatorframework eases the task of providing server-side validation into our application. Andfinally, well be introduced to the Tiles framework, with which we can compartmentalizeour presentation layer, as well as providing access to a templating mechanism for ourpages. DynaActionFormsEspecially in large applications, the number of classes that needs to be created andmaintained can become staggeringly high. Struts support classes can contribute a lot tothis number, especially with regards to its ActionForms, which require a solidimplementation for nearly all the forms in an application. A lot of developers chafedunder this restriction since ActionForms are mostly simple JavaBeans with get and setmethods for each of the form fields it needs to represent.Struts came up with a solution in its 1.1 version release, called DynaActionForms.DynaActionForms behave exactly like ActionForms, in that an instance of it can beobtained and its methods called upon by the Action handlers that need its data. Themain difference is, each DynaActionForm is not defined or declared as a separate class.A DynaActionForm is simply configured from within the struts-config.xml file.Below is an example of how to configure and declare DynaActionForms. We have madeuse of our example in the previous chapter, creating here an ActionForm that will handlethe data required for logging in.
<?xml version=”1.0”?><!DOCTYPE struts-config PUBLIC “-//Apache Software Foundation//DTD StrutsConfiguration 1.1//EN” “http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd” ><struts-config><form-beans><form-beanname=”loginForm”type="org.apache.struts.action.DynaActionForm"><form-property name=”loginName” type=”java.lang.String”/><form-property name=”password” type=”java.lang.String”/></form-bean></form-beans><action-mappings><action name=”loginForm”path=”/login”scope=”request”type=”login.LoginAction”><forward name=”success” path=”/success.jsp”/><forward name=”failure” path=”/failure.jsp”/></action></action-mappings></struts-config>As we can see, creating a DynaActionForm is very simple. In some cases declaring aDynaActionForm from within the configuration file is simpler and faster compared towriting an actual ActionForm instance. We no longer have to list down all of the formproperties and create get and set methods for each of them. With DynaActionForms, wesimply declare the property name and type. It is then the frameworks responsibility toprovide a working instance based on this information.Providing this configuration information is the only thing necessary to make use ofDynaActionForms. There are no changes that need to be made to any of our Actioninstances. As far as they are concerned, they still have a valid ActionForm instance thatthey can retrieve data from.The following are the Java types supported by DynaActionForm: • java.lang.BigDecimal • java.lang.BigInteger • boolean and java.lang.Boolean • char and java.lang.Character • double and java.lang.Double • float and java.lang.Float • int and java.lang.Integer • long and java.lang.Long • short and java.lang.Short • java.lang.String • java.lang.Date • java.lang.Time • java.sql.TimeStamp
While using DynaActionForms may be more convenient, they are not always the bestsolution. There are still cases where using ActionForms are more appropriate. • DynaActionForms only support a limited set of Java types. If our form needs to store information expressed as a data type other than those supported, then ordinary ActionForms are still the way to go. An example of this would be if we were to use custom Java objects as form properties. • DynaActionForms do not support the concept of inheritance. There is no way to create a base definition for a form that can be extended later on to provide for specifics. ValidatorsValidation is an activity that should be performed for all cases of data input. Byvalidation, we mean the checking of the correctness of the format and content of user-given values. Users, after all, do not always enter correct input: letters might be enteredinto a numeric only field and vice-versa; a field that requires 3 digits is given only 2, andso on. It is the applications responsibility to be aware of and handle such input errorsaside from any errors resulting from processing of business logic (password does notmatch for given login, etc.).Struts alleviates the developers burden in performing this validation by providing avalidation framework called the Validator Framework. Usage of this framework providesa couple of benefits: • The framework provides several pre-defined validation rules. There are common set of checks performed in any number of applications such as format checking, length checking, checking for existence, etc. The framework provides the components necessary such that developers no longer need to create code that will perform these types of validation. Generally, the components the framework provides are enough for most applications, though custom validators can also be created. • It eliminates redundancy in validation code. The framework separates the components performing the validation from the components needing validation. Instead of having multiple components incorporating the same validation code, the functionality can be externalized into a separate validation component that can be reused throughout the whole application. • It provides a single point of maintenance. Developers no longer need to go all over their application to check on the validation rules that they enforce on their various components. All such rules are declared in configuration files provided by the framework.There are several steps required in including Validator functionality within an existingStruts application: • Configure the Validator Plug-in. • Declare the forms requiring validation and the type of validation they require. • Create the messages that will be displayed in case of validation failure. • Modify the struts-config file to enable automatic validation.
Configuring the Validator Plug-InThis step is required to make the Struts framework aware of our usage of the Validatorframework. All that needs to be done is to add a few lines into our struts-config.xml file.Below is a sample:...<forward name=”success” path=”/success.jsp”/><forward name=”failure” path=”/failure.jsp”/></action></action-mappings><plug-in className="org.apache.struts.validator.ValidatorPlugIn"><set-propertyproperty="pathnames"value="/WEB-INF/validator-rules.xml, /WEB-INF/validation.xml"/></plug-in></struts-config>The pathnames property informs the framework where it can find the configuration filesthat it needs. There are two configuration files: validator-rules.xml and validator.xml. validator-rules.xmlThis configuration file defines the classes implementing the validation code. Theframework comes with a copy of this file with the pre-defined validator classes alreadyconfigured.The following is a list of the logical names of the validators that came shipped in with theframework: • required – properties marked as required must have at least one character aside from any whitespace. • mask – properties subjected to the mask validator must match the regular expression that we submit using the mask parameter. • minlength – if applied to a property, the property must have a length equal to or greater than the value of the min parameter that we pass. • maxlength - if applied to a property, the property must have a length equal to or less than the value of the max parameter that we pass. • range - the property must fall in between the min and max values that we supply using the min and max parameter • byte, short, integer, long, float, double – the property must be convertible to the specified primitive type. • date – succeeds if the value of the property is a valid date. • creditCard – succeeds if the value of the property can be a valid credit card number. • email – succeeds if the value of the property can be a valid email address.These predefined validators are enough for most validation purposes. In cases whereyour applications validation requirements cannot be met by these validators, custom
implementations can be created, and their definitions added to the validator-rules.xmlfile. The creation of custom validator components is an advanced topic and will not becovered in this course. validation.xmlThis configuration file declares to the framework which forms require validation andwhich validation rules it would like to implement. The framework only provides thestructure of the file. Developers would have to configure this file themselves to avail ofthe frameworks functionality. . Configuring the validation.xml fileThe validation.xml file provides a structure which we can use to specify our validationconfiguration. The following are the XML elements that it defines:<formset>The root element of this configuration file.<form>Each form in our application that requires validation should have a corresponding<form> element. The name attribute in this case should map to the name of a form-bean configured in struts-config.xml. This element can contain one or more <field>elements.<field>Each field element represents a property in the form that requires validation. Thiselement has the following attributes: • property – the name of the property within the form that will be validated. • depends – the comma-separated list of the validators that will be applied to the property. The names of the validators are defined within the validator-rules.xml file.<arg0>Defines the key to the error message that will be displayed in case the property does notpass validation rules. The key and the actual error message that will be displayed can befound in a resource bundle that the developer should create.<var>Some validators require certain values to be passed to them as parameters so that theycan function properly. These parameters can be supplied by using one or more varelements, with each var element corresponding to a parameter given to the validator.This element defines two child elements: • <var-name> - defines the name of the parameter to be supplied. • <var-value> - defines the value of the parameter.An example of such configuration file is provided below.
<formset><form name=”loginForm”><field property=”loginName” depends=”required”><arg0 key=”error.loginname.required”/></field><field property=”password” depends=”required,minlength”/><arg0 key=”error.password.required”/><var><var-name>min</var-name><var-value>4</var-value></var></field></form></formset>The sample file provided configures the loginForm used in our earlier examples.loginForm here directly corresponds to an ActionForm that is defined within the strutsconfiguration file under the name loginForm. This was expressed by setting loginForm asthe value to the name attribute of the <form> element.After the <form> element, we find that there are two <field> elements defined. The first<field> element configures the loginName property of our form, the second configuresthe password property. We can determine which is which by looking at the value of theproperty attribute. Defining the resource bundleThe <arg0> element above defined a key that needs to be matched to an entry in aresource bundle. This entry is then used to determine the message that will be displayedto the user. The Validator framework makes use of the same resource bundle that theStruts framework uses, which, by default, can be found in the WEB-INF/classes directoryunder the name ApplicationResources.properties. If such a resource bundle does notcurrently exist in your application, create a text file with that name.Entries in the resource bundle are simple key=value pairs. For the configuration exampleabove, we can have the following content:error.loginname.required=Please enter your login nameerror.password.required=You have supplied a blank password or a password with lessthan 4 charactersThe last step in enabling the validation framework is to change our ActionForm andDynaActionForm classes to make use of the classes provided. Making use of our earlierDynaActionForm example:
<?xml version=”1.0”?><!DOCTYPE struts-config PUBLIC “-//Apache Software Foundation//DTD StrutsConfiguration 1.1//EN” “http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd” ><struts-config><form-beans><form-beanname=”loginForm”type="org.apache.struts.validator.DynaValidatorForm"><form-property name=”loginName” type=”java.lang.String”/><form-property name=”password” type=”java.lang.String”/></form-bean></form-beans><action-mappings><action name=”loginForm”path=”/login”scope=”request”type=”login.LoginAction”validation=”true”><forward name=”success” path=”/success.jsp”/><forward name=”failure” path=”/failure.jsp”/></action></action-mappings></struts-config> TilesAnother framework that works especially well with Struts is the Tiles framework. UsingTiles, we can easily define templates and screen instances which we can use with ourapplication. What are templates?Simply put, a template page is a page layout design that can be reused by any of thepages in your application. Making use of templates gives your application a moreconsistent look and feel.To better appreciate the concept of templates, let us take a look at a couple of pagesfrom a web application:
Figure 2: Sample Page from a Web ApplicationLooking through the pages, we can see that there are several design elements commonfor all of them. All of them share the same set of navigational links on the left, the sameset of images on top of the page, the same set of images on the links on the bottom ofthe page. Most of what has changed between the different pages is the contentpresented in the middle of the page.Think of how we would be able to implement a set of similar pages. If we were toimplement each page as we see it, that is, exactly one page to correspond with each ofthe screens we can see, we would have trouble maintaining it in the future. That wouldbe because there would be a lot of code that would be duplicated – if later on someonedecided that the images on top didnt really look that good or that the navigational menuon the left isnt intuitive enough, the changes would have to be manually propagatedacross all of the pages.Being the programmers that we are, we know enough to refactor into separate entitiescode that would be duplicated across the application. This concept can also be applied toJSP/HTML pages: the navigational links could be implemented as one page, the headerimages as another, the footer yet another. These pages could then be added into a pageimplementing the body content. This can be performed without the use of anyframework, using the <jsp:include> action we have discussed in the basic JSP lecture.
A better solution would be to make the body content a separate JSP fragment itself, andto create a JSP page that defines the default formatting and layout of the pages, andsimply leaves placeholders for the other elements that could be included into the page. Adeveloper implementing a screen would then only have to make use of this "template"page and define a content page that the template would insert into the contentplaceholder.The benefits of this solution are obvious: any changes that would need to be performedon one aspect of the presentation layer, say the header page, would be applied to all ofthe pages while modifying only one page. If you wanted to change your sites layout,while still maintaining its content, it could be done by simply changing the templatepage.We will be learning how to do this templating using the Tiles framework. Preparing TilesBefore we can help ourselves to the benefits that the Tiles framework can give, we needto perform several preparatory steps.1. The first thing that we need to do is to add the following lines to struts-config.xml,just before the closing </struts-config> element:<plug-in className="org.apache.struts.tiles.TilesPlugin" ><set-propertyproperty="definitions-config"value="/WEB-INF/tiles-defs.xml" /></plug-in>This informs Struts that we want to make use of the Tiles framework, and that theconfiguration file that it would read can be found in the /WEB-INF directory, with thename tiles-defs.xml.2. Next, we need to copy the struts-tiles.tld and tiles-config.dtd files from the Struts libdirectory into the /WEB-INF directory of our application. The first file containsinformation on the custom tags that we will need to use for Tiles, and the second definesthe structure of the tiles-defs configuration file.3. Create a blank configuration file that we can fill up later. Make an xml file, name ittiles-defs.xml and place it in the WEB-INF directory. Then place the following content:<!DOCTYPE tiles-definitions PUBLIC"-//Apache Software Foundation//DTD Tiles Configuration//EN""http://jakarta.apache.org/struts/dtds/tiles-config.dtd"><tiles-definitions></tiles-definitions>After we have performed the previous steps, Tiles is ready for use.
Building a Layout templateThe first step in building a template is to identify the components that that will be placedin it. For example, most web pages have a header, footer, menu bar, and body ascomponents.If we were to draw a quick sketch, such a layout might look like the one in the diagrambelow. Figure 3: Basic layoutTo create a template page implementing this layout, follow these steps: 1. Create a new JSP page. 2. Import the Tiles tag library. 3. Create the HTML implementing the layout, leaving as blank the implementation for the actual components. 4. Use the <tiles:insert> tag to act as placeholders for the layout components.The HTML implementing the layout could be as simple as a table, with the componentsmodeled as table cells as shown in the JSP below:
<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %><HTML><HEAD><TITLE><tiles:getAsString name="title"/></TITLE></HEAD><BODY><TABLE border="0" width="100%" cellspacing="5"><TR><TD colspan="2"><tiles:insert attribute="header"/></TD></TR><TR><TD width="140" valign="top"><tiles:insert attribute="menu"/></TD><TD valign="top" align="left"><tiles:insert attribute="body"/></TD></TR><TR><TD colspan="2"><tiles:insert attribute="footer" /></TD></TR></TABLE></BODY></HTML>There are two custom tags in the above sample: <tiles:insert> and <tiles:getAsString>. • <tiles:insert> - has a number of uses in the Tiles framework. In this case, what it does is to insert a JSP fragment referred to by the name supplied in the attribute attribute. Which fragment corresponds to the name will be specified in the screen definition that will make use of this template. • <tiles:getAsString> - this tag simply retrieves as a String a value supplied in a screen definition using the name in the name attribute. Creating Screen DefinitionsOnce we have a template, we can make use of it to wholly define a screen. Creatingscreen definitions can be done in two ways within the Tiles framework: the definitionscan be defined within JSP pages, or within an XML file recognized by the framework. . Creating a definition using JSP pagesCreating a definition within a JSP page makes use of more custom tags supplied by Tiles: • <tiles:definition> - base tag with which to define a screen. The value of the id attribute is the name by which this definition can be referred to by other components. The value of the page attribute is the location of the template file it will use as its base.
• <tiles:put> - this tag is used to place a value into a specified attribute inside a template. The name attribute specifies the name of the target location inside the template. The value attribute defines the location of the JSP fragment to be used.Consider the example below:<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %><tiles:definition id="welcomePage" page="/layout/basicLayout.jsp"><tiles:put name="title" value="Welcome!"/><tiles:put name="header" value="/header.jsp"/><tiles:put name="footer" value="/footer.jsp"/><tiles:put name="menu" value="/menu.jsp"/><tiles:put name="body" value="/welcome.jsp"/></tiles:definition>In this sample, we create a screen definition for a welcome page. It makes use of thelayout we defined earlier and places the specified title, header, footer, menu, and bodycomponents into locations defined in the template.By default, this definition is visible only within the JSP page containing the declaration.To change this, we can supply a value into the optional scope attribute of the<tiles:definition> tag. The possible values are: page, request, session, and application. . Creating a definition using an XML configuration fileThe second way of creating a screen definition is by placing a configuration entry in thetiles-defs.xml configuration file that we initially created. The syntax in creating an entryis very similar to the <tiles:definition> used earlier:<!DOCTYPE tiles-definitions PUBLIC"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN""http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"><tiles-definitions><definition name="welcomePage" page="/layout/basicLayout.jsp"><put name="title" value="Welcome!"/><put name="header" value="/header.jsp"/><put name="footer" value="/footer.jsp"/><put name="menu" value="/menu.jsp"/><put name="body" value="/welcome.jsp"/></definition><!-- ... more definitions ... --></tiles-definitions>What is the difference between the two methods? Both methods load the definition as aJavaBean into memory. The JSP method though, loads the definition only after the JSPfragment containing it has been accessed and, by default, makes it visible only in thesame page. This condition makes reuse of the screen definition throughout theapplication a bit more problematic, since extra caution must be taken to avoid loadingand unloading the definition into and from memory unnecessarily.The XML method, on the other hand, makes the screen definition available to the whole
application immediately after startup. The Tiles framework takes care of persisting themin memory. The only thing that components require to make use of the definition is tosupply its name.Another benefit of using the XML method for creating the screen definition is that thedefinitions themselves can be used as ActionForwards by the Struts framework. This candrastically cut down the number of JSP files required by our application. Using definitionsas ActionForwards is a topic that will be discussed in more detail later. . Using the screen definitionsCreating the screen definition is not enough for it to be displayed to the user. To put aDefinition into use, we can use the <tiles:insert> tag and supply it the name of thedefinition to display:<%@ taglib uri="http://jakarta.apache.org/struts/tags-tiles" prefix="tiles" %><tiles:insert beanName="welcomePage" flush="true"/>The above page is all that is needed to display the welcome screen to the user.One of the problems with this approach is that it increases the number of JSPs requiredto display different screens to the user: aside from a body content component, eachscreen would require a separate page that would make use of the screen definition. Forapplications that requires a 100 or so screens, the number of pages that would need tobe created is doubled!A better approach would be to make use of the definitions as the targets ofActionForwards. By including the Tiles framework as a plugin for Struts (one of thepreparatory steps we performed), Struts is made aware of the screen definitions createdusing Tiles. The screen names can be used in place of actual locations in <forward>tags. Consider the example below:<action-mappings><action name=”loginForm”path=”/login”scope=”request”type=”login.LoginAction”validation=”true”><forward name=”success” path=”/welcomePage”/><forward name=”failure” path=”/failure.jsp”/></action></action-mappings>Here we have modified our earlier sample of struts-config.xml so that the success logicalcondition is now mapped to our welcomePage definition. As we can see, this approach issimple, easy to use, and cuts down on the number of JSP files that needs to be created. . Extending definitionsOne of the strengths of the Tiles framework over other templating methods is that it
allows the extension of screen definitions. Screen extension works in much the sameway as class inheritance in Java: the extended screen inherits all the properties andattributes of the parent Definition. This allows us to create a base definition screen thatdeclares default values for attributes that can be extended by Definitions specialized forspecific pages.Take the following example:<definition name="basicDefinition" page="/layout/basicLayout.jsp"><put name="header" value="/header.jsp"/><put name="footer" value="/footer.jsp"/><put name="menu" value="/menu.jsp"/></definition><definition name="welcomePage" extends="basicDefinition"><put name="title" value="Welcome!"/><put name="body" value="/welcome.jsp"/></definition><definition name="otherPage" extends="basicDefinition"><put name="title" value="My title"/><put name="body" value="/otherContent.jsp"/></definition>By creating a base screen that can simply be extended, we avoid repetition of attributevalue definition. Also, when a change needs to be made with regards to whichcomponent is placed in one of the base attributes, this change is immediately andautomatically propagated to all of the screens by simply making the change on the basescreen definition.EXERCISESThe exercise of this chapter will build on the exercise introduced in the earlier chapter.Students should form the same groupings as done in the last exercise.1) Using the Validator framework, include validation code for all the forms in theprevious exercise. -- Download Material Management -- Add Download Material form -> Update Download Material form -> Delete Download Material form -> Input is required, must be of integer format. -- User Management -- Add User form -> Update User form -> Delete User form -> input is required, must be of integer format.2) Convert all ActionForms used in the previous exercise into its DynaActionFormequivalent.3) Implement the screens defined in the previous exercise using the Tiles framework.The layout to be used is the basic layout defined earlier in this chapter. This activity
consists of several tasks: a) Separate the actual content previously created for each page into separate "body"pages. b) Create side bar pages consisting of a list of links applicable for each subsection ofthe application. For example, if the user is an administrator and is in the Main ContentPage, he would be able to see links to the Download Material Management page, UserManagement page, and User Activity page, as well as a link that he could click to logoutof the application. In general, the side bars should be implemented such that they: - Present a link to its parent page as defined in the screen flow definition in theprevious exercise. - Present links to the next set of pages that can be accessed according to the samescreen flow. - Present a link that will enable the user to log out of the application. c) Create Tiles definitions for each of the screens in the application and use theminstead of ActionForwards.