Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Enable backwards navigation through Web applications

Improve user experience with the Webflow Navigation Manager Framework

Maurizio Albari (maurizio_albari@it.ibm.com), Application development IT architect, IBM Italy
Maurizio Albari is an enterprise Java architect with eight years of experience. He works for IBM Italy within IBM Global Services Application Management Services. His chief areas of interest are J2EE, aspect-oriented programming, and adaptive programming languages. He is the author of the WebFlow Navigation Manager framework.

Summary:  Web frameworks like Struts and JavaServer Faces focus only on forward navigation of Web applications. In this article, Maurizio Albari introduces a framework that improves the backwards navigation of Web applications by keeping a server-side navigation history of visited Web pages and visited named sequences of Web pages, also known as Webflows. With the framework, you can also use the server-side navigation history to automatically clean the HTTP session, thus improving application performance. And the best part is that you can still use your favorite Web framework for forward navigation.

Date:  16 Nov 2004
Level:  Intermediate
Also available in:   Chinese

Activity:  7244 views
Comments:  

The navigation of a Web application is quite different from the navigation of a static Web site. When you navigate a static Web site, you can use the Back and Forward buttons of your browser and jump from one page to another with no problems, or bookmark your favorite page and return to it at any time. With the Back button of the browser, you can navigate backwards through the Web site, and your browser can cache Web pages without any worry because the page shown does not have to reflect the current state of any server-side applications.

When you navigate a Web application, things are different. Using the Back and Forward buttons can produce an incorrect state in the server application; typically, a Web browser cannot cache Web pages because the pages have to reflect the current state of the server application. The Back button of the browser is not typically the best way for the user to navigate in reverse through a Web application. A better solution is to keep the navigation history on the server side. With the WebFlow Navigation Manager framework, you can do just that.

The WebFlow Navigation Manager framework

The WebFlow Navigation Manager framework (which I'll call WFNM for short) is a Web framework that focuses on problems that current frameworks, such as Struts or JavaServer Faces (see the Resources for links to both), do not manage. Rather than trying to reinvent the wheel, it complements and can be used in conjunction with these other frameworks, and can even be used with applications based on servlets and JSP pages alone. WFNM is distributed under the Lesser GNU Public License, and binaries containing WFNM code can thus be used in any commercial Web application.

The WFNM framework provides two main types of functionality to your application:

  • Improving the backward navigation of the Web application
  • Automatically cleaning your HTTP sessions

Backwards navigation is improved by introducing the concept of Webflow, which is a named sequence of visited Web pages. In fact, the backward navigation is improved at two different levels:

  • Page level: The framework keeps a server-side navigation history of visited Web pages, making it easy to develop server-side actions that take the user back to the previous page (or even reload the current one).
  • Webflow level: That same server-side navigation history also makes it easy to for users to get back to the previous Webflow, or even a previously visited Webflow, when given its name.

The above capabilities are also used by WFNM to provide an automatic session cleaning mechanism that allows developers to use HTTP sessions more safely. If code uses HTTP sessions poorly, that leads to inappropriate consumption of application and physical memory. The automatic session cleaning mechanism provided by WFNM allows the developer to split the HTTP session into different scopes, and introduces into Web applications a mechanism that is similar to the garbage collector mechanism of Java Virtual Machines.

It is difficult to imagine a programming language without the concept of variable scope, or a Java Virtual Machine without the garbage collector. Yet most of today's Web applications are developed without such concepts. In essence, the WFNM framework tries to solve just these problems. Now, I'll show you how it works in more detail.


Server-side navigation history of Web pages

To better understand the motivation behind WFNM, look at this example. Imagine a Web application with the navigation described in Figure 1. The application consists of four pages: b1, b2, b3, and b4.


Figure 1. Pages b1, b2, b3, and b4
Pages b1, b2, b3 and b4

Frameworks like Struts or JavaServer Faces can be configured, typically through an XML configuration file, to manage the forward navigation among these pages. But such frameworks typically do not handle dynamic backwards navigation.

For pages b2 and b3, the only possible previous page is b1, so a configuration file-based approach can be used to navigate backwards from those pages. But what about page b4? b4 has two possible previous pages, b2 and b3, depending on the forward path the user took to reach it. In this scenario, server-side actions can use a server-side navigation history of visited pages to return the user to the correct previous page.

Assume that you have a Java factory that is able to calculate the previous page given a session like that in Figure 1:


Listing 1. Get the previous page
String url = DynamicFactory.getPreviousPage(session);

You can use this information to force your favorite Web framework (if any is used) to return to the previous page. But how can you implement a Java factory with such a feature? For the moment, it doesn't matter; assume that, as if by magic, you have just such a factory: I will discuss how to implement it later in this article.


Server-side navigation history of Webflows

Web applications are not typically monolithic. They are composed of several parts, each one with a specific function.

Often, some parts of a Web application are common to many use cases. For example, a Java method invokes another method (which can itself also be called by other methods) and, when it ends, returns control to the calling method. Similarly, in a Web application, different pages can call part of the application; when the called functionality has run its course, the control returns to the caller page.

As mentioned above, I call a part of a Web application (that is, a sequence of visited Web pages) a Webflow. Each Webflow is labeled with a name.

Now I can introduce a more complex scenario that contains three Webflows: B (yellow pages b1, b2, b3, and b4), C (orange pages c1, c2, c3, and c4), and D (green pages d1, d2, and d3), as illustrated in Figure 2.


Figure 2. A more complex scenario
A more complex scenario

The same navigation issues described for Web pages are still valid for Webflows: What happens if a user on page d3 (in Webflow D) wants to return to the previous Webflow (that is, the last visited page of the previous Webflow), which can be Webflow B (page b4) or Webflow C (page c4)?

Clearly, in this scenario, server-side actions can use a server-side navigation history of visited Webflows for returning to the previous Webflow (such as, the last visited page of the previous Webflow) or even to a specific previous Webflow, when given its name.

Assume, for example, that you have a Java factory that can calculate the previous Webflow given a session, as shown in Listing 2, or a previously visited named Webflow, as shown in Listing 3.


Listing 2. Get the previous Webflow
String url = DynamicFactory.getPreviousWebflow(session);


Listing 3. Get a previous named Webflow
String url = DynamicFactory.getPreviousWebflow(session,"A");

With this information, you can force your favorite Web framework (if any is used) to return to the last visited page of the previous Webflow.

At any time during the navigation of the Web application, the controller can get the name of the current Webflow, as shown in Listing 4:


Listing 4. Get the current Webflow name
String webflowName = DynamicFactory.getCurrentWebflowName(session)

The controller also can discover whether a Webflow was visited during the course of the user's navigation, as shown in Listing 5:


Listing 5. Determine if a Webflow has been visited
boolean visited =  DynamicFactory.isWebflowVisited (session,"B");


Define Webflows

To achieve all this, you need a simple way to define Webflows. In other words, how can you label a sequence of visited Web pages with a name?

You can take inspiration from the way that version 3 of the Enterprise JavaBeans specification uses annotations to add metadata to Java source code. Instead of a separate XML file (which can get out of sync with the related code when it changes), assume that you have some Web tag to indicate that a page is an entry for a Webflow. Listing 6 shows how this would work.


Listing 6. Define a Webflow
<wfnm:webflow name="D"/>

In the example illustrated in Figure 2, this tag is applied to both pages d1 and d2, as they are both part of Webflow D. Similar tags must be applied to pages b1 and c1, assuming that they are entry points for the Web application or can be called from a previous hypothetical Webflow A. (You don't need to add these tags to every page, though you could for completeness.)


A complete scenario

Many Web applications have a login page. Sometimes it is the very first page and it protects the whole Web application; in addition, many Web applications have a home page.

In this instance, the login page is called a1 and the home page is called a2. They are parts of a Webflow called A. Combine this with the example shown in Figure 2, and now you have a complete Web application, as illustrated in Figure 3.


Figure 3. A complete scenario
A complete scenario

The user opens a browser, types a URL, and reaches the login page a1. If the authentication executes successfully, then the user reaches the home page a2. The Web application allows two use cases: one starting from page b1 and one starting from page c1.

The use cases are composed of two steps each: the first use case needs Webflow B and Webflow D, the second use case needs Webflows C and D; Webflow D is common to both use cases.

In this scenario, during the navigation of Webflow D, it is easy return to the previous Webflow by using the DynamicFactory.


Navigation history implementation

To implement a server-side navigation history of visited pages and Webflow, you can use a stack of stacks data structure: a stack of visited Webflows, each containing a stack of visited pages.

For example, imagine that the navigation of the Web application follows this path:

a1->a2->b1->b2->b4->d1->d3

In such a case, the state of the stack of stacks data structure is similar to Figure 4.


Figure 4. A sample stack of stack status
A sample stack of stack status

Clean the session automatically

Most Web applications are similar to the scenario presented above. The navigation starts from a home page (a2), follows a use case, and then provides navigation back to the home page. Typically, updates to enterprise information systems such as relational databases are persisted during the use case, so usually objects placed into the HTTP session are no longer useful when returning to the home page.

But how many developers remember to remove objects from the HTTP session when they are no longer necessary -- that is, at the end of the use case, just before returning back to the home page?

A structured approach for automatically cleaning the HTTP session when objects are no longer useful guards against potential use of expired objects. It also provides developers the freedom to put more objects into the HTTP session, since they can be sure that such objects are automatically removed by a framework when they are no longer necessary. These added objects can potentially improve the user experience.

HTTP session cleaning improves Web application performance because it uses the HTTP session less and the application server saves an important resource: memory. In the same way that a firewall guarantees security of communications in a network environment, an automatic session cleaning framework guarantees that objects are kept in the HTTP session only when needed. It should work using a simple default policy, with exceptions handled on a case-by-case basis.

But how to implement a framework with the above features? Assume that, as if by magic, you are able to tie each object placed into the HTTP session during the navigation to a specific element of the stack of stacks data structure illustrated in Figure 4 -- that is, to a page or a Webflow. An object tied to an element is owned by that element.

The algorithm might be: an object is automatically removed from the HTTP session when its owner is removed from the stack (the internal stack for pages or the external stack for Webflows). This means that an object owned by a page is removed when the user returns to a previous page, whereas an object owned by a Webflow is removed when the user returns to a previous Webflow. Depending on the policy used to tie each object placed into the HTTP session to an element of the stack of stacks data structure, you can define a kind of scope for that object.

To better understand the concept of ownership, look at the stack of stacks data structure together with objects owned by each element. For example, you are using Struts and the Web pages are in the /jsp/struts directory (relative to the document root). If the current Webflow owns objects placed into the HTTP session during navigation, the stack of stacks data structure looks something like Figure 5.


Figure 5. Current Webflow ownership status
Current Webflow ownership status

If the current page owns objects placed into the HTTP session during the navigation, the stack of stacks data structure looks like Figure 6.


Figure 6. Current page ownership status
Current page ownership status

Choose the right policy

When you choose the policy to tie an object to a page or a webflow, it is important to define both its scope and the time at which the object is removed from the HTTP session. The policy must be flexible enough to handle all possible situations and minimize the configuration effort. You want to configure the framework at three levels:

  • Overall framework: The framework has a default configuration, which is valid for all Webflows and all objects placed into the HTTP session. Implement it using a centralized configuration singleton, which you can configure with a properties file. You might insert a line like the one in Listing 7 into such a file.


    Listing 7. Set the default ownership
    net.sf.wfnm.DEFAULT_OWNERSHIP=...
    



  • Single Webflow: If the default configuration for Webflows is not appropriate for a specific Webflow, you might want to change the policy just for that Webflow. Do this, for example, by specifying the owner in the tag that defines the Webflow, as shown in Listing 8.


    Listing 8. Set the ownership for a specific Webflow
    <wfnm:webflow name="D" owner="..."/>
    



  • Single object: If an object is placed into the HTTP session with a specific key, tie it to a particular owner. You can do so by using an appropriate tag, as shown in Listing 9.


    Listing 9. Set an object ownership
    <wfnm:owner key="objectKey" owner="..."/>
    

Before I go on to describe the values that the owner attribute can assume, I want to discuss naming conventions. Given that the first Webflow in the stack is usually the home page and that the others are use cases that typically end by returning to the first Webflow, I'll call the first Webflow the global Webflow and the subsequent Webflows working Webflows.With that in mind, consider the potential values of the owner attribute:

  • page: The page reached through navigation owns the object; it is removed when the user returns to a previous page.
  • webflow: The current Webflow reached through navigation owns the object; it is removed when the user returns to a previous Webflow.
  • previous: The previous Webflow owns the object; it is removed when that previous Webflow is removed from the stack.
  • working: The working Webflow owns the object; it is removed when the working Webflow ends and the user returns to the global Webflow.
  • global: The global Webflow owns the object; it is removed when exiting the framework (if a <wfnm:reset/> is encountered, for instance).
  • none: No Webflow owns the object; it is never automatically removed by the framework.

For example, you might globally configure the framework with a line in a properties file like the one in Listing 10.


Listing 10. Set the default ownership to Webflow
net.sf.wfnm.DEFAULT_OWNERSHIP=webflow

If this default is not appropriate for a particular Webflow, you can define that Webflow with a different ownership, as in Listing 11.


Listing 11. Set a Webflow ownership to previous
<wfnm:webflow name="D" owner="previous"/>

And if this default is not appropriate for a particular object, you can define the ownership of that object that is placed into the HTTP session, as in Listing 12.


Listing 12. Set an object ownership to global
<wfnm:owner key="objectKey" owner="global"/>


What about the magic?

I previously assumed that you were able to magically implement the following features:

  • The ability to keep track of visited Web pages and visited Webflows
  • The ability to tie each object to an element in the stack of stacks data structure

For the first issue, assume that you have a Java factory that you can use as shown in Listing 13.


Listing 13. The notify factory
NotifyFactory.notifyPage(request);

Here, request is an HttpServletRequest.

If a J2EE 1.4 environment is available, you can use this factory to track visited pages and Webflows with a simple filter, shown in Listing 14.

If you assume that Web pages are in a /jsp directory (relative to the document root), configure the filter in the web.xml file as shown in Listing 15.


Listing 15. The PageNotifierFilter XML configuration
<filter>
 <filter-name>wfnmPageNotifierFilter</filter-name>
 <filter-class>net.sf.wfnm.Web.PageNotifierFilter</filter-class>
</filter>

<filter-mapping>
 <filter-name>wfnmPageNotifierFilter</filter-name>
 <url-pattern>/jsp/*</url-pattern>
 <dispatcher>FORWARD</dispatcher>
 <dispatcher>REQUEST</dispatcher>
</filter-mapping>

Given that the capability of a filter to handle both request and forward was only introduced with J2EE 1.4, older versions of J2EE require a different solution. For these versions, you'll need to add a tag to the very end of each JSP page (or to a common template, if a template mechanism such as Tiles is used). You might apply a tag like the one in Listing 16 to each page.


Listing 16. The notify tag usage
<wfnm:notify/>

The implementation of the tag might use the above NotifyFactory, as shown in Listing 17.


Listing 17. The notify tag implementation
public class NotifyTag extends TagSupport {
 ...
 public int doStartTag() throws JspException {
  NotifyFactory.notifyPage((HttpServletRequest) pageContext.getRequest());

  return SKIP_BODY;
 }
}

You can solve the second issue -- the ability to tie each object to an element in the stack of stacks data structure -- with a filter as well. Suppose that you have a wrapper for the HTTP session called HttpSessionWrapper. This wrapper implements the HttpSession interface by delegating methods to an original session, which can be passed in the constructor. The implementation of the methods setAttribute(String,Object) and removeAttribute(String,Object) can also notify the framework that an attribute has been added or removed from the HTTP session. Assume that the HttpSessionWrapper has the static method illustrated in Listing 18.


Listing 18. The HttpSessionWrapper
public static HttpSession wrapItIfNecessary(HttpSession session) {
 if (session instanceof HttpSessionWrapper) {
  return session;
 } else {
  return new HttpSessionWrapper(session);
 }
}

You can use the above class to define a custom HttpServletRequestWrapper that extends the javax.servlet.http.HttpServletRequestWrapper as shown in Listing 19.

Now you can build a simple filter, illustrated in Listing 20, that replaces the ServletRequest with the HttpServletRequestWrapper in Listing 19.

By replacing the original request with this wrapper, the class returned when one of the getSession(...) methods is invoked is a class that notifies the framework if an object has been added or removed from the original HTTP session.


Performance

To understand why it's really important to automatically clean the HTTP session, run a performance test using the previous example.

For each forward navigation step, assume that a bean that is one kilobyte in size is stored in the HTTP session. Repeat the test twice, with and without automatic session cleaning.

The testing navigation consists of four round trips:

Round tripNavigation path
1a1->a2->b1->b2->b4->d1->d3->a2
2b1->b3->b4->d1->d3->a2
3c1->c2->c4->d2->d3->a2
4c1->c3->c4->d2->d3->a2->a1

The performance results are described in Figure 7: the dashed, red line is the size of the session without automatic session cleaning; the solid, blue line is the size of the session with automatic session cleaning.


Figure 7. Session size performance
Session size performance

Use the code

The best way to start with the WFNM is to look at the sample provided with the framework itself at the project's Web site (see Resources): it is the complete scenario described in this article. It is largely self-explanatory and can be used as a template for developing other Web applications. The sample contains two different versions of the complete scenario shown above: the first is implemented using WFNM in conjunction with Struts, the second is implemented in conjunction with JavaServer Faces.

Once you've examined the demo at the Web site, download wfnm-sample-<version>.zip, unpack it, and deploy the file wfnm-sample.war, which is located into the build directory, into your favorite J2EE 1.4 Web container. Now you can play with the live WFNM sample Web application.

Look at the Java source files, which are located under the src directory; in particular, look at the class net.sf.wfnm.sample.struts.StrutsDynamicFactory and of the class net.sf.wfnm.sample.jsf.FacesDynamicFactory. These classes use the DynamicFactory class of the WFNM framework. Now look at how Web actions use the above factories to navigate backwards through your Web applications. In addition, you'll want to study how the classes net.sf.wfnm.sample.jsf.HomeBean and net.sf.wfnm.sample.struts.HomeAction set the default configuration of the WFNM framework.

Next, unpack wfnm-sample.war and examine web.xml, which is located in the WEB-INF directory. You'll see the filters the WFNM framework needs. Also, look at the JSP pages contained in the jsp directory of wfnm-sample.war to see how WFNM tags are used to define Webflows.

If you plan to develop a Web application with Struts or JavaServer Faces, you can take the WFNM sample itself as a template. web.xml contains configuration elements for both frameworks. Whether you use one of these frameworks or just strike out on your own, you'll just need to remove the elements you don't need.

Obviously, you have to remove from the sample any resource you don't need, except the wfnm.tld file, located in the WEB-INF directory and wfnm.jar itself, which is located into the WEB-INF/lib directory. If you use Struts or JavaServer Faces, move the appropriate class (StrutsDynamicFactory or FacesDynamicFactory, respectively) to your Web application Java package. Otherwise, if you plan to develop a Web application just with servlets and JSP pages, or even with another Web framework such as Spring or WebWork, simply look at how the StrutsDynamicFactory and FacesDynamicFactory classes use the DynamicFactory class of the WFNM framework.

For example, if you plan to use just servlets and JSP pages and you need to get your users back to the previous page, you can just use the code in Listing 21:


Listing 21. Move back to the previous page
public void doGet(HttpServletRequest req, HttpServletResponse res) throws ... {
	String url = DynamicFactory.getPreviousPage(req.getSession());
  	getContext().getServletDispatcher(url).forward(req,res);
}

In a similar way you, can use the other methods of the DynamicFactory to navigate backwards through your Web application.

The automatic session cleaning needs no special configuration, but remember to change the default policy when necessary. For example, if objects placed into the HTTP session during Webflow D should still be kept in the HTTP session when returning to the previous Webflow B, remember to specify this policy when defining Webflow D, as shown in Listing 22:


Listing 22. Specify session cleaning policy
  <wfnm:webflow name="D" owner="previous" />

In this way, objects placed into the HTTP session during Webflow D are owned (and still visible) by Webflow B, and removed only when returning to Webflow A.


In conclusion

This article has defined the concept of a Webflow as a named sequence of Web pages and suggests a way of implementation using a Web tag. By examining a sample scenario, you saw how to improve the backward navigation of a Web application by maintaining a server-side navigation history of Web pages and Webflows, and how to implement it using a filter. You also saw that you can use server side navigation history for automatically cleaning the HTTP session (choosing among different policies), and how you might implement such a system. Finally, you saw how automatic session cleaning can drastically reduce the average usage of the HTTP session.

You can achieve all of these benefits by integrating the WFNM framework into your Web application. Download WFNM from Resources to get started!


Resources

About the author

Maurizio Albari is an enterprise Java architect with eight years of experience. He works for IBM Italy within IBM Global Services Application Management Services. His chief areas of interest are J2EE, aspect-oriented programming, and adaptive programming languages. He is the author of the WebFlow Navigation Manager framework.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=31918
ArticleTitle=Enable backwards navigation through Web applications
publish-date=11162004
author1-email=maurizio_albari@it.ibm.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers