
Beginning Apache Struts - From Novice To Professional (2006)
.pdf338 |
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
1.Go to the Sun JSF download site (http://java.sun.com/j2ee/javaserverfaces/ download.html) and download the latest JSF reference implementation. Extract the relevant JSF binaries, jsf-api.jar and jsf-impl.jar, and place them in the
.\registration\lib folder.
2.Extract jstl.jar and standard.jar, which are the JSTL binaries, from the Struts distribution zip file in the Source Code section of the Apress website to the
.\registration\lib\ folder.
3.From the struts-faces.zip file in the Source Code section of the Apress website, extract the JAR file called struts-faces.jar, and place it in .\registration\lib.
As you should know by now, the scripts for the Registration webapp copy the files in
.\registration\lib and place them in the webapp’s /WEB-INF/lib folder upon deployment.
Step 3: Edit web.xml and struts-config.xml
To initialize JSF and get the Struts-Faces library and struts-config.xml files to work, you’ll need to follow these steps:
1.Declare the Standard Faces servlet. In web.xml, put in the following declaration:
<servlet> <servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup>
</servlet>
You need to pay special attention to the <load-on-startup> value; it has to be the lowest one declared. If you have other servlets declared, then ensure that their <load-on-startup> values are greater than the one associated for FacesServlet. This ensures that FacesServlet gets initialized first.
2.Put in the standard JSF servlet mapping. In web.xml, put in the following servlet mapping:
<servlet-mapping> <servlet-name>faces</servlet-name> <url-pattern>*.faces</url-pattern>
</servlet-mapping>
This tells Tomcat that the page URLs ending with .faces should be shuttled to the servlet called faces, which as you can see from the earlier <serlvet> declaration is
FacesServlet.

C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
339 |
3.Put in a special Struts Controller. In your struts-config.xml file, put in the following
<controller> element (just before the <message-resource> tag):
<controller>
<set-property property="processorClass" value="org.apache.struts.faces.application.FacesRequestProcessor"/>
</controller>
■Note The registration webapp doesn’t use Tiles, so we’ve used the controller shown in the last step. However, if your application does use Tiles, use FacesTilesRequestProcessor instead.
Step 4: Migrate Your Struts JSP Pages
The Struts-Faces TLD file contains tags that you can use in place of the Struts HTML and Bean tag libraries. You are expected to use JSTL instead of the Struts Logic tag library and nested properties syntax instead of the Nested tag library. The preceding comments apply to the Struts-EL tags as well. You should continue to use the Tiles tag library as it is, though.
If you have an existing Struts application, you can migrate to JSF incrementally—you don’t have to do it all in one go. If you have to migrate an existing app or you’re creating a new one, there are a few things you need to do for each page.
First, declare the JSTL, JSF, and Struts-Faces taglibs in your JSPs. These must be in the following order:
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<%@ taglib prefix="s" uri="http://struts.apache.org/tags-faces" %>
Obviously, you should remove declarations to HTML, Bean, Nested, or Logic tag libraries on the page.
Next, put in the replacement tags. Table 20-1 shows the tags in the Struts-Faces taglib, which are equivalents of certain tags in the HTML or Bean taglib.
You could use <s:loadMessages> instead of <f:loadMessages>. The difference between the two is that <s:loadMessages> will make your Struts-declared Application.properties file available to JSF tags.
These replacement tags have attributes similar to their Struts counterparts, but in some cases, they are quite rudimentary. A prime example is <s:message>, which does not allow replacement arguments, as <bean:message> does. In this instance, you should use the much more powerful <h:outputFormat> tag, which does allow replacement arguments.

340 |
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
Table 20-1. Struts-Faces Tags and Their Nearest Pure Struts Equivalents
Tag Name |
Struts Counterpart |
s:errors |
html:errors |
s:form |
html:form |
s:commandLink |
html:link |
s:html |
html:html |
s:write |
bean:write |
s:message |
bean:message |
s:javascript |
html:javascript |
|
|
But, in order for JSF tags to read your Application.properties file, you’d have to expose it with <s:loadMessages> like so:
<s:loadMessages var="messages"/>
...
<h:outputFormat value="#{messages['app.logon.prompt.success']} "> <f:param value="#{LogonForm.userid}"/>
</h:outputFormat>
When there is no equivalent tag, you’d use an appropriate JSF or JSTL tag instead. Table 20-2 contains a few common JSF replacements you can use.
Table 20-2. Struts Tags and Their JSF Equivalents
Struts Tag Name |
JSF Replacement |
<html:text property="myProp"> |
<h:inputText id="myprop" value="#{myFormBean.myProp}" > |
<html:password ...> |
<h:inputSecret ...> |
<html:hidden ...> |
<h:inputHidden ...> |
<html:submit > |
<h:commandButton id="submit" type="SUBMIT"...> |
<html:reset > |
<h:commandButton id="reset" type="RESET"...> |
<html:cancel > |
<h:commandButton id="cancel" type="SUBMIT"...> |
|
|
In your migration efforts, you’ll have to follow standard JSF rules, like putting in the enclosing <f:view> tag. If you’re migrating Struts-EL tags, be sure to replace ${...} with the JSF EL’s #{...}. The same applies, of course, to the Struts-Faces tags, since they are just JSF extensions.
Now, change the form handler names. In your <s:form>s, you need to change the form handler name from *.do to the path declared in struts-config.xml. For example, if you had

C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
341 |
<html:form action="MyFormHandler.do" ...
this would now be
<s:form action="/MyFormHandler" ...
Note the leading slash.
Finally, make the necessary changes to .\registration\web\index.jsp. You’ll probably have to refer to the JSF examples and notes from the previous section.
Step 5: Migrate the <forward>s and Inputs
Lastly, you need to migrate the <forward>s that point to the JSPs you’ve migrated.
■Caution Please pay careful attention here—only change a <forward> if you’ve migrated the JSP it points to!
For each such forward, change the .jsp suffix of the target page to .faces. For example, change
<forward name="login" path="/login.jsp" />
to
<forward name="login" path="/login.faces" />
Do not amend the actual extensions of the corresponding JSP pages! Similarly, you need to change declarations of all affected input attributes on your <action-mapping>s to use the .faces extension. For example, you’d change
<action .... |
input="/login.jsp” ... |
to |
|
<action .... |
input="/login.faces" ... |
You should only change the inputs that point to migrated pages.
Step 6: Make Entry Points Forward to *.faces
All entry points to your webapp must forward to *.faces if that page was migrated to JSF. For example, suppose the start page for your app was registration.jsp, which contained Struts tags. You then migrate this page to JSF. You can’t call the page directly (refer to the sidebar “Faces Requests” in the JSF introduction section earlier for details) from the browser.

342 |
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
You need to call it indirectly from another page, say index.jsp. This page would contain a forward to registration.faces:
<jsp:forward page="registration.faces" />
Note that there is no leading slash. Make the appropriate change for the Registration webapp.
Step 7: Amend Actions if Necessary
Throughout this book, we’ve asked you to use ActionMessages if you want to report complex validation errors in your Action subclasses—and rightly so, since the old ActionErrors class has been deprecated.
Unfortunately, the Struts-Faces integration library (version 1.0) only works with this deprecated ActionErrors class! If you use the kosher ActionMessages class, a ClassCastException is thrown when you redisplay a page after a complex validation failure.
I’m sure that this will be remedied in the next version of the library, but in this lab, you’re stuck with changing the ActionMessages instance in RegistrationAction to ActionErrors.
Do this now, then compile and deploy your app. You should be able to register a user. Test that all simple and complex validations work. If you can’t get everything to work, ensure that you haven’t made the following mistakes:
•Used #{} instead of ${} in your JSTL tags: JSF tags use the former while JSTL uses the latter.
•Failed to manually give unique IDs to every JSF tag nested within any JSTL tags: The symptom of this is a “Duplicate ID error” or something similar. In this case, simply give unique IDs to each JSF tag (or JSF extension tag) nested within any JSTL tags. Usually, you can get away with giving the <s:form> or <h:form> an ID.
If all else fails, don’t be ashamed to take a peek at the answers in the lab-20-answers.zip file in the Source Code section of the Apress website.
■Note Due to licensing restrictions, the answers zip file does not have the Sun JSF reference implementation. You have to download this yourself.
Step 8: Put in the Necessary <managed-bean> Declarations
This step addresses another shortcoming of the Struts-Faces integration library that you might have noticed if you’ve got step 7 working: it’s that the RegistrationForm form bean
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
343 |
does not get instantiated when you click the browser’s refresh button after successfully registering.
This can be easily fixed by fooling JSF to create the necessary form bean for you. You do this by creating a blank faces-config.xml file, then putting in a <managed-bean> section with the bean name exactly the same as the one in struts-config.xml (in our case, it’s
RegistrationForm).
This is a kludge, and I’m sure the problem will be addressed in future iterations of the Struts-Faces library.
In a Nutshell
We’ve only scratched the surface of the Struts-Faces integration library in this lab session. If you’re interested, you should try out the sample apps that come with the distribution.
You may also have noticed that except for using ActionErrors you don’t have to amend the Action or ActionForm subclasses of your app. This of course is a huge plus, and is in most cases much more desirable than migrating to a whole new framework like Shale.
However, you should be aware that the Struts-Faces library won’t always work. In an initial draft for this section, I attempted to migrate the login webapp of Chapter 14, only to discover that you can’t port Tiles that forward their views to other JSPs, which is what the login webapp does. This is probably a JSF restriction, so it’s difficult to see how such issues might be resolved in future versions of the Struts-Faces library. It might be possible to amend the Tiles tags so that they work better with the Struts-Faces integration library.
The lesson here is that you need a new framework that addresses such issues—a reworking of Struts from the ground up. That’s what Shale is all about.
Struts Shale Preview
As we’ve mentioned before, Shale is based on JSF. This means it inherits all of JSF’s advantages, like a highly customizable View tier. But Shale adds many benefits on top of JSF. In this section, we’ll walk you through the primary value-added areas of Shale. These fall into two broad categories:
•A set of services and service options. (Don’t confuse these with web services. The services here are akin to Struts plug-ins, and service options are simply ways to configure Shale and its services.) Services include integration with the Validator framework and a reworking of Tiles, now called “Clay.” A significant new addition on top of these is Shale’s Dialog Manager, which makes control flow more transparent. The services were designed with customization in mind, so Shale is easier to customize and extend compared with Struts.
•Integration with other frameworks and technologies (Spring, Ajax, JNDI, etc.).
344 |
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
In my opinion, these and the advantages of JSF make Shale a worthy successor of Struts. Unfortunately, technical superiority alone does not guarantee survival, and for the reasons we’ve outlined in the sidebar at the start of this chapter (“Struts Ti and Struts OverDrive”), Shale’s “successorship” from Struts classic is by no means a done deal.
ViewController
In JSF, backing beans are Plain Old Java Objects (POJOs). This gives you a lot of flexibility in creating your backing beans, but it also means that your beans have to do a lot of things “by hand.”
A good example is a realistic implementation of the user backing bean (see Listing 20-10). In my implementation, I used a HashMap to simulate a database. In an actual implementation, you might have to create and release database connections, and these might be best centralized instead of being repeated in functions like Logon() and Register().
Shale provides an interface called ViewController (see Listing 20-13), which contains a few event-handling functions and a variable called postback.
Listing 20-13. Shale’s ViewController Interface
/********************************************************************** Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
**********************************************************************/
package org.apache.shale.view;
public interface ViewController{
/**
* Returns true if the page is called the second time around. */
public boolean isPostBack();
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
345 |
/**
* Sets the "post back" flag. */
public void setPostBack(boolean postBack);
/**
*Called after the JSF request processing lifecycle has been
*completed for the current request.
*/
public void destroy();
/**
*Called after this ViewController backing bean has been
*instantiated, and after all of the property setters specified
*above have been called, but before the JSF request processing
*lifecycle processing has started.
*/
public void init();
/**
*Called after the UI tree has been restored. So, it will NOT be
*called if the page is displayed for the first time.
*/
public void preprocess();
/**
*Called before the "Render Response" phase. This method
*will be called only for the view that will actually be
*rendered. For example, it will not be called if you have
*performed navigation to a different view.
*/
public void prerender();
}
If your managed bean implements the ViewController interface, Shale will call the event-handling functions on your ViewController implementation at the appropriate times (refer to the comments in Listing 20-13).

346 |
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
The idea is for you to centralize resource creation or destruction activities in these functions.
■Note The class AbstractViewController implements ViewController, with a functioning implementation for isPostBack() and setPostBack(), and no-op implementations for the event handlers. If you wish, you can extend this class for convenience.
One important gotcha is that you have to have the right name for your <managed-bean>. Not any old name will do! Your managed bean’s name (we’re referring to the name declared in faces-config.xml, not the classname of the backing bean) will have to match the name of the associated the JSP page (referred to as a view identifier). Here are some examples:
•If the view identifier is /logon.jsp, then the managed bean must be named logon.
•If the view identifier is /admin/logon.jsp, then the managed bean must be named admin$logon.
•If the view identifier is named /header.jsp, then the managed bean must be named _header, because header alone is reserved in JSF.
From this, you can see that there’s a one-to-one relationship between managed beans and your JSP pages. This is in contrast to the one-to-many relationship we’ve used for the registration webapp.
All this might seem a little abstract, so we’ll apply the ViewController idea to a “realistic” example—the Registration webapp, where database connections need to be created and released. Listing 20-14 shows this class, with the logic stripped out for clarity.
Listing 20-14. User Backing Bean, Take 2
package net.thinksquared.reg;
//...other import statements omitted
org.apache.shale.view.AbstractViewController;
public class User extends AbstractViewController{
Connection _connection; //connection to database
//...other private variables
|
C H A P T E R 2 0 ■ J A V A S E R V E R F A C E S A N D S T R U T S S H A L E |
347 |
// |
...constructor |
|
//... |
data get/set |
|
//... |
UI get/set |
|
//------------------------------------- |
Actions |
|
public String Register(){ |
|
|
|
//use _connection to read and write to database |
|
}
public String Logon(){
//use _connection to read and write to database
} |
|
//------------------------------------- |
for ViewController |
public void |
init(){ |
//create |
_connection to database. |
}
public void destroy(){
//release _connection to database.
}
}
You can see in Listing 20-14 how we’ve taken advantage of the facilities on ViewController to centralize creation and release of the database connection.
Of course, because Shale assumes a one-to-one mapping between managed beans and Views, the <managed-bean> declaration (see Listing 20-8) has to be changed as shown in Listing 20-15.