Web Application Best Practices


Now that you have reviewed some of the key concepts related to Web applications in WebLogic Server, it s time to dig in and discuss best practices. So many options are available to designers and developers of J2EE Web applications that it would require an entire book to list and explain all of the Web application best practices we could conceivably discuss. In this section, we ve attempted to discuss the best practices we feel are applicable to the widest variety of development efforts or are most likely to improve the quality or performance of your WebLogic Server Web applications.

The best practices contained in this chapter cover everything from recommended techniques for using custom tags to proper packaging of your Web application to caching page content for performance. They are presented in no particular order of importance, as the importance of a given best practice depends greatly on the particular application you are building.

Ensure Proper Error Handling

Unhandled exceptions that occur during the execution of a servlet or JSP-generated servlet cause the processing of that page to stop. Assuming the response has not been committed, the JSP output buffer will be cleared and a new response generated and returned to the client. By default, this error response contains very little useful information apart from the numeric error code.

What you need is a friendly, informative error page containing as much information as possible to help during debugging. Fortunately, there is a built-in mechanism for specifying a custom error page for use in handling server errors during processing.

First, you construct an error page JSP to present the error information to the user in a friendly fashion. At a minimum, it should display the exception information and a stack trace. To be more useful during debugging, it can display all request and HTTP header information present using the same methods employed by SnoopServlet , discussed earlier. Portions of an example error page are shown in Listing 1.1. The entire page is available on the companion Web site (http://www. wiley .com/compbooks /masteringweblogic).

Listing 1.1:  ErrorPage.jsp.
start example
 <%@ page isErrorPage="true" %> <html> <head><title>Error During Processing</title></head> <body> <h2>An error has occurred during the processing of your request.</h2> <hr> <h3><%= exception %></h3> <pre> <%     ByteArrayOutputStream ostr = new ByteArrayOutputStream();     exception.printStackTrace(new PrintStream(ostr));     out.print(ostr); %> </pre> <hr> <h3>Requested URL</h3> <pre> <%= HttpUtils.getRequestURL(request) %> </pre> <h3>Request Parameters</h3> <pre> <% Enumeration enum = request.getParameterNames(); while(enum.hasMoreElements()){     String key = (String)enum.nextElement();     String[] paramValues = request.getParameterValues(key);     for(int i = 0; i < paramValues.length; i++) {         out.println(key + " : "  + paramValues[i]);      } } %> </pre> <h3>Request Attributes</h3> <pre> ... </pre> <h3>Request Information</h3> <pre> ... </pre> <h3>Request Headers</h3> <pre> ... </pre> 
end example
 

Second, place a < %@ page errorPage= ... % > directive on all JSP pages in the application specifying the location of this error JSP page. Listing 1.2 presents a simple example JSP page that declares the error page explicitly. Normally you would do this through a common include file shared by all pages rather than including the directive on every page.

Listing 1.2:  ErrorCreator.jsp.
start example
 <%@ page errorPage="ErrorPage.jsp" %> <html> <head></head> <body> <!-- Do something sure to cause problems --> <% String s = null; %> The string length is: <%= s.length() %><p>  </body> </html> 
end example
 

Accessing the ErrorCreator.jsp page from a browser now causes a useful error message to be displayed to the user. The page could conform to the look and feel of the site itself and could easily include links to retry the failed operation, send an email to someone, or go back to the previous page.

As an alternative to specifying the errorPage on each individual JSP page, a default error-handling page may be specified for the entire Web application using the error-page element in web.xml :

 <error-page>     <error-code>500</error-code>     <location>/ErrorPage.jsp</location> </error-page> 

These two mechanisms for specifying the error page may look very similar but are, in fact, implemented quite differently by WebLogic Server. The < %@ page errorPage= ... % > directive modifies the generated servlet code by placing all JSP scriptlet code, output statements, and other servlet code in a large try/catch block. Specifying the error page in web.xml does not affect the generated servlet code in any way. Instead, uncaught exceptions that escape the _jspService() method in the original page are caught by the Web container and forwarded to the specified error page automatically.

Which technique is best? Unless the target error page must differ based on the page encountering the error, we recommend the error-page element in web.xml for the following reasons:

  • A declarative and global technique has implicit benefits over per-page techniques. Individual pages that require different error pages can easily override the value in web.xml by including the page directive.

  • The information describing the original page request is more complete if the error-page element is used rather than the page directive. Specifically, calling request.getRequestURL() in the error page returns the URL of the original page rather than the URL of the error page, and additional attributes are placed on the request that are not present if the page directive is employed. Note that WebLogic Server correctly includes the special javax.servlet. error.request_uri attribute in the request after forwarding to the error page using either the error-page element or the page directive, so there is always at least one consistent way to retrieve the original page name .

The examples available for this chapter include error-creation pages using both techniques for your examination. ErrorCreator.jsp uses the page directive, and BadErrorCreator.jsp simply creates an error without specifying an error page, thereby relying on the error-page element in web.xml to specify the correct error page. Accessing these two pages from your browser and observing the output will help you understand the differences in request information available depending on the technique used to declare the error page.

Best Practice  

Create a friendly and useful error page, and make it the default error page for all server errors using the error-page element in web.xml . Override this default error page using the page directive in specific pages, if necessary.

Use jsp:useBean to Reduce Scriptlet Code

The jsp:useBean element provides a powerful mechanism for declaring beans on a JSP page. Beans are given names by this element and may be declared in different scopes: page, request, session, and application. The scope determines the bean s availability in other servlets and page requests :

  • Page scope places the bean reference in the PageContext and makes it available in subsequent scriptlet code, elements, and custom tags during this page processing only. This is the default scope if no scope attribute is present in the jsp:useBean element.

  • Request scope places the bean reference in the HttpServletRequest using setAttribute() , making it available on this page and in any pages included during this processing cycle using jsp:include or jsp:forward elements.

  • Session scope places the bean reference in the HttpSession object for this client, making it available on this page and in all subsequent requests by this particular client until removed from the session.

  • Application scope places the bean in the WebApplicationContext , which makes it available to any page in this particular Web application until the application server is shut down or the Web application is redeployed.

In its simplest form, the jsp:useBean element can be considered a shorthand for scriptlet code that establishes a bean instance in the given scope. For example, consider the element shown here:

 <jsp:useBean id=currentrez class=examples.Reservation /> 

This can be considered equivalent to the following scriptlet code:

 <% examples.Reservation currentrez = new examples.Reservation(); %> 

The true advantage of jsp:useBean is not apparent until you use a scope other than page . For example, the following element declaring the Reservation object to be in the session scope requires significant coding in the equivalent scriptlet. The jsp:useBean element is straightforward:

 <jsp:useBean id=currentrez               class=examples.Reservation scope=session /> 

The corresponding scriptlet code is fairly complex:

 <%  Object obj = session.getAttribute("currentrez"); if (obj == null) {     obj = new examples.Reservation();     session.setAttribute("currentrez", obj); } examples.Reservation currentrez = (examples.Reservation)obj;  %> 

Clearly, there is an advantage to using the jsp:useBean element in this case. Note that the name of the bean, defined by the id attribute in jsp:useBean , will be used as the key in the getAttribute() call to find the bean in the HttpSession object. It is important that the name be unique enough to avoid naming conflicts with other items placed in the session.

Best Practice  

Declare beans used in JSP pages using jsp:useBean , especially when the bean is in the request, session, or application scope. Use names descriptive enough to avoid naming conflicts with other beans defined at the same scope.

Beans declared using jsp:useBean are available in subsequent scriptlet code and in special jsp:getProperty and jsp:setProperty elements used to access and modify bean attributes. These special elements can be used to eliminate some scriptlet code from your JSP pages. For example, the jsp:getProperty element eliminates the need for expression scriptlets when displaying data in beans, as shown in Listing 1.3.

Listing 1.3:  ShowPerson.jsp.
start example
 <%@ page import="mastering.weblogic.ch01.example1.Person" %> <jsp:useBean id="pp" class="mastering.weblogic.ch01.example1.Person"              scope="request" /> <html> <head><title>Show the Person Data</title></head> <body> Here is the Person from the request:<BR> First Name: <  jsp:getProperty name="pp" property="firstName"/  ><BR> Last Name: <  jsp:getProperty name="pp" property="lastName"/  ><BR> Age: <  jsp:getProperty name="pp" property="age"/  ><BR> </body> </html> 
end example
 

This represents a slight improvement in readability over the equivalent code using expression scriptlets, and it may prove easier to maintain for nonprogrammers working with the visual elements on JSP pages.

The jsp:setProperty element calls set methods on the related bean, passing in data supplied in the element or from the current HttpServletRequest object, depending on the attributes supplied in the element. For example, in the JSP action page shown in Listing 1.4, the jsp:setProperty elements interrogate the HTTP request and place data for lastName , firstName , and age in the corresponding bean attributes:

Listing 1.4:  EnterPerson_action.jsp.
start example
 <%-- Create a "Person" object, load it with data from request params,       and store it on http request --%> <%@ page import="mastering.weblogic.ch01.example1.Person" %> <jsp:useBean id="pp" class="mastering.weblogic.ch01.example1.Person"              scope="request" /> <  jsp:setProperty name="pp" property="lastName" /  > <  jsp:setProperty name="pp" property="firstName" /  > <  jsp:setProperty name="pp" property="age" /  > <jsp:forward page="ShowPerson.jsp" /> 
end example
 

The jsp:useBean element also includes an initialization feature allowing the execution of scriptlet code or custom tags if the declared bean was not found in the specified scope. The initialization code may also use jsp:setProperty elements to initialize the bean and may perform any operations normally allowed in JSP scriptlet code. This feature is useful when declaring a bean in the request or session scope that may already have been defined by an earlier page in the process. For example, the following element declares a bean at the session scope and initializes its attributes using the jsp:setProperty elements if it was not already present in the session:

 <jsp:useBean id=pp class= mastering.weblogic.ch01.example1.Person              scope=session>     <jsp:setProperty name=pp property=lastName value=Nyberg />     <jsp:setProperty name=pp property=firstName value=Greg />     <jsp:setProperty name=pp property=age value=39/> </jsp:useBean> 

We will make limited use of the jsp:useBean element and related jsp:getProperty and jsp:setProperty elements during the construction of the sample Web application in Chapters 3 and 4.

Use Custom Tags for Selected Behaviors

Custom tags are a powerful mechanism for extending the basic JSP tag syntax to include custom-developed tags for interacting with Java components , modifying response content, and encapsulating page logic. As with jsp:useBean elements, using custom tags can reduce or eliminate the need for scriptlet code in the JSP page and improve maintainability. Custom tags are more powerful and flexible than jsp:useBean elements because they allow the manipulation of JSP content and provide a much richer interface.

The power of custom tags comes with a cost, of course: complexity. Unlike jsp:useBean elements, which are essentially a shortcut for common tasks typically done through scriptlet code, custom tags add an entirely new layer to the architectural picture and require a strictly defined set of classes and descriptor files to operate . While a detailed description of the steps required to create custom tags is beyond the scope of this text, it is instructive to review the key concepts to frame the recommendations we will be making.

Custom Tag Key Concepts

Custom tags require a minimum of three components:

  • The Tag Handler Class is a Java class implementing either the javax.servlet .jsp. tagext .Tag or BodyTag interfaces. The Tag Handler Class defines the behavior of the tag when invoked in the JSP page by providing set methods for attributes and implementations for key methods such as doStartTag() and doEndTag() .

  • The Tag Library Descriptor (TLD) File contains XML elements that map the tag name to the Tag Handler Class and provide additional information about the tag. This file defines whether the tag contains and manipulates JSP body content, whether it uses a Tag Extra Information (TEI) class, and the name of the library containing this tag.

  • JSP Pages contain < %@ taglib ... % > declarations for the tag library and individual tag elements in the page itself to invoke the methods contained in the Tag Handler Class.

Custom tags may also define a Tag Extra Information (TEI) class, extending javax.servlet.jsp.tagext.TagExtraInfo , that defines the tag interface in detail and provides the names and types of scriptlet variables introduced by the tag. During page generation, the JSP engine uses the TEI class to validate the tags embedded on the page and include the correct Java code in the servlet to introduce scriptlet variables defined by the custom tag.

Custom Tag Use Is Easy ”Development Is Complex

It is important to keep the appropriate goal firmly in mind when evaluating a new technology or feature for potential use on your project. In the case of jsp:useBean elements or custom tags, the goal is normally to improve the readability and maintainability of the JSP pages. The assumption is that by reducing or eliminating scriptlet code the page will be easier to understand and maintain, which is true enough. But the JSP pages are only one part of the total system being developed. The beans and custom tags are part of the system as well, and any improvement in maintainability of the JSP pages must be weighed against the complexity and maintenance requirements of the beans and tags themselves .

Custom tag development, in particular, is complex. The complexity is not evident until the tasks being performed become more realistic, perhaps requiring TEI classes, body content manipulation, handling of nested tags, or other more advanced behaviors. Examine the source code for some tag libraries available in the open -source community (see the libraries in http://jakarta.apache.org/ taglibs , for example) to get a sense of the requirements for a realistic, production-ready tag library. Is your development team ready to tackle this level of development? Are the people being earmarked for maintenance of the application capable of maintaining, extending, or debugging problems in the tag library? These are valid questions you should consider when making your decision to build a custom tag library.

Using custom tags, on the other hand, is relatively easy. It requires a simple declaration at the top of the JSP page and a few straightforward XML elements in the page to invoke the custom tag and produce the desired behavior. Although there may be cases when scriptlet code is still the appropriate solution, we recommend using custom tags for most development efforts.

In the end, the decision comes down to the benefits of using custom tags versus the effort to develop and maintain the custom tags. Clearly a tag that is developed once and used on many pages may pay for itself through the incremental benefits accrued across multiple uses. Taken to the limit, the most benefit will come from a tag used in many pages that is acquired rather than internally developed, eliminating all development and maintenance effort on the tag itself. This should be your goal: Use custom tags, but don t develop them.

Best Practice  

Custom tags are easy to use but difficult to develop and maintain, so make every effort to locate and use existing tag libraries from reputable sources rather than developing your own custom tags.

Many useful tag libraries are available from various vendors and open-source communities. Table 1.4 provides a short list to get you started in your search.

Table 1.4:  Custom Tag Sources

Location

Description

http://jakarta.apache.org/taglibs

This source has a large number of open-source tag libraries, providing everything from string manipulation to regular-expression handling to database access.

http://jakarta.apache.org/struts

Struts is a model-view-controller framework that includes a number of useful tag libraries.

http://www.servletsuite.com/jsp.htm

This commercial vendor, with more than 80 different tag libraries, offers free binary download and evaluation.

http://www. sourceforge .net/projects/jsptags

This source has open-source tag libraries created by members of the SourceForge community. Many different libraries and functions are included.

http://www.jsptags.com

This is a good reference site that lists many available tag libraries.

In addition, BEA packages a few custom tags in the WebLogic Server product, including a very useful caching tag we will examine in the next section.

We will be using selected custom tags from the Struts framework in the example application in Chapters 3 and 4 to display bean data and create HTML form elements with automatic handling of posted data during processing.

Cache Page Output to Improve Performance

Caching is a time-honored mechanism to improve performance. Database products use caching to improve throughput, application servers use caching to improve EJB performance, and many applications include caching in the business or services layer to avoid costly calculations or data access. All of these layers of caching are important, but in a Web application the surest way to improve performance is to cache the page output itself whenever possible because caching page output can completely eliminate calls to the business services and data-access routines.

Custom tags provide a powerful mechanism for caching page output because tags are allowed to access and modify the content placed in their body and skip the processing of that body content during subsequent invocations. In other words, a properly designed custom tag can surround a section of page output, allow the page to process normally the first time, store the generated HTML response, and use the stored response instead of processing the content for subsequent requests.

WebLogic Server includes a caching custom tag called wl:cache in the weblogic-tags tag library. This tag can cache page output based on any key value in the session, request, page, or application scope, can cache at any one of these scopes, and can be set to cache for a limited or indefinite time. Caching is performed using Java system memory for maximum performance, unlike some open-source page-caching tags that use disk storage.

Some simple examples will show you how the wl:cache custom tag works. The format for the wl:cache tag, when used to cache output, is this:

 <wl:cache name=... key=... scope=... timeout=... size=...> ... // Body content to be cached.. // Can be HTML, JSP scriptlets, directives, other tags, etc. ... </wl:cache> 

In the simplest form, with no key or scope information supplied in the attributes, the wl:cache tag caches the generated body content for the specified length of time for all clients (because application scope is default):

 <wl:cache timeout="60s"> ... // Body content to be cached.. ... </wl:cache> 

Listing 1.5 shows the CacheTest1.jsp example program that demonstrates the use of the caching tag in this simple manner. The content above the wl:cache tag is evaluated with every page invocation, but the content in the tag is evaluated the first time the page is accessed by any user and cached for 60 seconds.

Listing 1.5:  CacheTest1.jsp.
start example
 <%@ taglib uri="weblogic-tags.tld" prefix="wl" %> <HTML> <BODY> Current time is: <%= System.currentTimeMillis() %><br> <wl:cache timeout="60s"> <% System.out.println("Inside cached body"); %> Cached time is: <%= System.currentTimeMillis() %><br> </wl:cache> </BODY> </HTML> 
end example
 

Accessing this JSP page repeatedly will produce browser output similar to the following:

 Current time is: 1015363376897 Cached time is:  1015363376897 Current time is: 1015363385004 Cached time is:  1015363376897 ... 

The displayed cached time will remain unchanged in the output during subsequent page hits because the contents of the body, including the call to System.currentTimeMillis() , are not evaluated in the generated servlet. The System.out .println() log message in the body content will help confirm that the body is not evaluated on subsequent invocations. After 60 seconds, the cache will expire, the body content will be evaluated during the next page request, and the cached HTML response will be updated with the new output.

Even this simple behavior might be useful in a real application because the wl:cache tag can wrap any arbitrary JSP content, even directives such as jsp:include . Recall that jsp:include is used to include content generated by other JSP pages within the current page at page-processing time, often as an alternative to the page-generation-time < %@ include file= ... % > directive. If your display pages are built up from multiple component parts (header, footer, navigation bars, etc.) using many separate jsp:include directives to include the parts, a simple wl:cache tag placed around these directives can dramatically improve performance. The CacheTest2.jsp example program in Listing 1.6 illustrates this technique.

Listing 1.6:  CacheTest2.jsp.
start example
 <%@ taglib uri="weblogic-tags.tld" prefix="wl" %> <HTML> <BODY> <  wl:cache timeout="5m"  > <TABLE BORDER="1">   <TR>     <TD><jsp:include page="CacheTest2_header.jsp"/></TD>   </TR>   <TR>     <TD><jsp:include page="CacheTest2_navbar.jsp"/></TD>   </TR>   <TR>     <TD>       <TABLE BORDER="1">         <TR>           <TD><jsp:include page="CacheTest2_leftside.jsp"/></TD> <  /wl:cache  > <TD>This is the main content for the page..</TD> <  wl:cache timeout="5m"  >           <TD><jsp:include page="CacheTest2_rightside.jsp"/></TD>         </TR>       </TABLE>     </TD>   </TR>   <TR>     <TD><jsp:include page="CacheTest2_footer.jsp"/></TD>   </TR> </TABLE> <  /wl:cache  > </BODY> </HTML> 
end example
 

The first time this page is executed, the HTML response generated by the content in the sets of wl:cache tags will be cached in memory. Subsequent page requests will avoid the multiple jsp:include operations during the page processing and the performance hit that goes with them.

Best Practice  

Look for opportunities to cache static, or relatively static, content using the wl:cache custom tag. Caching the results of jsp:include operations can improve performance significantly.

The first two example programs limited themselves to key-less caching, meaning that the content was cached and reused for the specified period of time regardless of client identity, or any parameter or value present during the page processing. In most cases, however, the generated page content depends on some contextual information such as a request parameter or scriptlet variable. Fortunately, the wl:cache tag includes a powerful and flexible mechanism for caching content based on parameters and other context information through the key attribute in the wl:cache tag definition:

 <wl:cache key=[parameterpagesessionrequestapplication].keyname ...> 

The important assumption is that the body content depends on the value of the key or keys, so caching must also depend on these values. For example, if the body content depends on the value of a request parameter called howmany , the wl:cache tag must include this parameter in the key attribute. The CacheTest3.jsp example program in Listing 1.7 illustrates this case.

Listing 1.7:  CacheTest3.jsp.
start example
 <%@ taglib uri="weblogic-tags.tld" prefix="wl" %> <HTML> <BODY> <  wl:cache name="CacheTest3" key="parameter.howmany" timeout="5m"  > <%  int jj = Integer.parseInt(request.getParameter("howmany")); System.out.println("Inside cached body with howmany of " + jj);  %> <H2>Were going to count from 1 to <%= jj %><H2> <% for (int ii = 1; ii <= jj; ii++) {     out.print(ii + "<br>"); } %> <  /wl:cache  > </BODY> </HTML> 
end example
 

Accessing this page with a specific value of howmany in the query string causes the body content, including the loop and System.out.println() code, to be executed one time. Subsequent page hits with the same howmany parameter value return the same content without reevaluating the content. Supplying a different value for howmany will cause the body to be evaluated for that value and the contents cached using that key value. In other words, if you hit the page five times with different howmany values, you ve created five different cached versions of the body content using howmany as the key. This technique is very slick and very powerful for improving site performance.

Two of the optional attributes in the wl:cache tag provide important capabilities:

  • The size attribute limits the size of the cache to a certain value. If the cache is key dependent and there are many possible key values, it is a good idea to limit the size of the cache to something reasonable (perhaps 500 to 1,000 entries) using this attribute.

  • The name attribute is used to identify this cache within the overall set of caches managed by the wl:cache tag library. If you omit this attribute, the name will be a unique combination of the request URI and tag index in the page. This may be sufficient in simple cases. If the cache must be flushed, however, a name should be specified to allow a different wl:cache tag to be able to refer to the same name and flush the cache.

Content that depends on multiple parameters or context values can be cached by combining the parameters in the key attribute using a comma separator or by combining the multiple values ahead of time in a single scriptlet variable and placing that variable in the key attribute using the page.varname syntax. This code snippet uses multiple key parts:

 <wl:cache key=parameter.howmany,parameter.color timeout=5m> ... </wl:cache> 

This is effectively the same as this snippet:

 <% String ss = request.getParameter(howmany) + , +             request.getParameter(color); %> <wl:cache key=page.ss timeout=5m> ... </wl:cache> 

In both cases, the content is cached and reused depending on the value of both the howmany and color request parameters.

How would caching work in a real application that displays business information using something like entity beans or value objects retrieved from a stateless session bean, two architectures common in J2EE applications? The JSP page used to display the bean content places the content-generation code in a wl:cache tag that depends on the value of the primary key for the bean. Subsequent page hits for the same bean information will then use the cached content.

The trick, of course, is that the underlying bean data may change, and the cached display page will continue to use the cached HTML output for that particular bean until the time-out period expires . You need to be able to flush the cache for a particular key value when the content is no longer valid in order to force the next page request to retrieve the bean and display the latest data. The wl:cache tag includes an optional attribute called flush for this purpose. You would flush the cache used by CacheTest3.jsp , for example, using a tag like this:

 <wl:cache name=CacheTest3 key=parameter.howmany flush=true /> 

Note that there can be no body content when the wl:cache tag is used to flush a cache.

Generally speaking, this tag should be executed immediately after the bean is updated in the database. As we will discuss in Chapter 2, most large Web applications use servlets rather than JSP pages for processing bean changes. It is awkward , if not impossible , to call a JSP custom tag within a servlet to perform an activity like flushing the cached content for this particular bean.

One reasonable solution is to include logic in the display JSP page to sense a particular flushcache request parameter and conditionally perform the flush early in the processing of the page if this parameter is present. The servlet that performed the bean update will normally forward processing to the display JSP page after completing the update, and it is easy enough to include the flushcache parameter in the request before forwarding.

Best Practice  

Use the key-specific caching capability of the wl:cache custom tag to cache page content for specific request parameters and beans whenever possible.

Use Servlet Filtering for Common Behaviors

Servlet filtering, a new feature of servlets introduced in the Servlet 2.3 specification, provides a declarative technique for intercepting HTTP requests and performing any desired preprocessing or conditional logic before the request is passed on to the final target JSP page or servlet. Filters are very useful for implementing common behaviors such as caching page output, logging page requests, providing debugging information during testing, and checking security information and forwarding to login pages. Figure 1.2 illustrates the basic components of the filtering approach and shows the incoming HTTP request passing through one or more Filter classes in the FilterChain collection defined for this page request.

click to expand
Figure 1.2:  Servlet filtering.

Placing a filter in the path of a particular servlet or JSP request is a simple two-step process: Build a class that implements the javax.servlet.Filter interface, and register that class as a filter for the desired pages and servlets using entries in the web.xml descriptor file. To illustrate this process, we will build and deploy a simple but useful filter that intercepts servlet and JSP requests and logs HttpServlet Request information before passing the request on to the intended JSP page or servlet. We ll call the filter SnoopFilter because it is very similar to the SnoopServlet discussed previously.

Building a Simple SnoopFilter Filter Class

The first step is the construction of a filter class called SnoopFilter that implements the javax.servlet.Filter interface and performs the desired logging of request information. Simply put, the doFilter() method writes information from the HttpServletRequest object to System.out before forwarding to any additional filters in the filter chain. The source for SnoopFilter is available from the companion Web site (http://www.wiley.com/compbooks/masteringweblogic).

Registering SnoopFilter in the web.xml Descriptor File

Registering a filter requires a set of elements in the Web application descriptor file, web.xml . These elements declare the filter class and define the pages or servlets to which the filter should be applied. In this simple example, you want all pages and servlets in the application to be filtered through SnoopFilter , and the web.xml file includes the following elements:

 <filter>   <filter-name>SnoopFilter</filter-name>   <display-name>SnoopFilter</display-name>   <description></description>   <filter-class>     mastering.weblogic.ch01.example1.SnoopFilter   </filter-class> </filter> <filter-mapping>   <filter-name>SnoopFilter</filter-name>   <url-pattern>/*</url-pattern> </filter-mapping> 

The < url-pattern > /* < /url-pattern > element declares that all pages and servlets in the application should be filtered using SnoopFilter , so every page request will go through the filter before normal processing begins. The server s stdout stream will therefore contain detailed request information for every page request, which is potentially very useful during development and debugging.

Clearly the same general logging capability could have been placed in a helper class, custom tag, or simple scriptlet included in each JSP page or servlet, but the ability to control the specific pages or groups of pages using the SnoopFilter in a declarative manner (via url-pattern elements) has significant advantages.

Although this is obviously a simple example, SnoopFilter illustrates the value of filters for preprocessing activities such as logging, auditing, or debugging in J2EE Web applications. Filters are not limited to writing output to stdout; they can easily write information to separate log files, insert rows in database tables, call EJB components, add or modify request attributes, forward the page request to a different Web application component, or perform any other desired behavior unconditionally or based on specific request information. They are a very powerful addition to the J2EE servlet specification.

Best Practice  

Use filters to implement common behaviors such as logging, auditing, and security verification for servlets and JSP pages in your Web applications.

Response Caching Using the CacheFilter

WebLogic Server includes a filter called CacheFilter that provides page-level response caching for Web applications. This filter is very similar to the wl:cache custom tag, discussed earlier in this chapter, except that it operates at the complete page level rather than surrounding and caching only a section of JSP content in a page. The CacheFilter may also be used with servlets and static content, unlike the custom tag, which works only for JSP pages.

The CacheFilter is registered like any other servlet filter. Define the filter in the web.xml file, and specify the url-pattern of the page or pages to cache. Use initialization parameters in the filter registration to define time-out criteria and other cache control values similar to the wl:cache custom tag. For example, to cache the response from a specific JSP page for 60 seconds, register the CacheFilter using elements similar to the following:

 <filter>   <filter-name>CacheFilter1</filter-name>   <filter-class>weblogic.cache.filter.CacheFilter</filter-class>   <init-param>     <param-name>timeout</param-name>     <param-value>60</param-value>   </init-param> </filter> ... <filter-mapping>   <filter-name>CacheFilter1</filter-name>   <url-pattern>CacheFilterTest1.jsp</url-pattern> </filter-mapping> 

The JSP page will execute the first time the URL is accessed by any client, and the content of the HTTP response will be cached by the filter and used for all subsequent access requests for 60 seconds.

Additional initialization parameters for the CacheFilter include the following:

Name.    The name of the cache. The wl:cache tag may be used to flush the CacheFilter cache using this name and the flush= true attribute. It defaults to the request URI.

Timeout.    Timeout period for the cached content. It defaults to seconds, but it may be specified in units of ms ( milliseconds ), s (seconds), m (minutes), h (hours), or d (days).

Scope.    The scope of the cached content. Valid values are request, session, application, and cluster. Note that CacheFilter does not support page scope. It defaults to application scope.

Key.    The name of request parameters, session attributes, and other variables used to differentiate cached content. It is similar in function to the key attribute in the wl:cache custom tag.

Size.    The maximum number of unique cache entries based on key values. It defaults to unlimited.

As a final example, recall that the CacheTest3.jsp page examined earlier used the wl:cache tag to cache the generated content based on the howmany request parameter because the page response depended on the value of that parameter (see Listing 1.7). The equivalent CacheFilter implementation would require the following entries in the web.xml file:

 <filter>   <filter-name>CacheFilter3</filter-name>   <filter-class>weblogic.cache.filter.CacheFilter</filter-class>   <init-param>     <param-name>timeout</param-name>     <param-value>5m</param-value>   </init-param>   <init-param>     <param-name>key</param-name>     <param-value>parameter.howmany</param-value>   </init-param> </filter> ... <filter-mapping>   <filter-name>CacheFilter3</filter-name>   <url-pattern>CacheFilterTest3.jsp</url-pattern> </filter-mapping> 

The key parameter indicates that the page-level cache should be segregated based on the value of the howmany parameter in the request. A separate cached response will be created for each value of this parameter and used for five minutes before reevaluating the underlying page. Note that the CacheFilter class must be registered using a different filter-name element each time in order to supply different initialization parameters. The CacheFilterTest3.jsp page is very similar to CacheTest3.jsp (Listing 1.7), but it no longer requires the wl:cache custom tags:

 <HTML> <BODY> <%  int jj = Integer.parseInt(request.getParameter(howmany)); System.out.println(Inside cached body with howmany of  + jj);  %> <H2>Were going to count from 1 to <%= jj %><H2> <% for (int ii = 1; ii <= jj; ii++) {     out.print(ii + <br>); } %> </BODY> </HTML> 

The CacheFilter approach has an obvious advantage over the wl:cache technique in this example: Caching is performed using a declarative technique rather than embedding custom tags in the page itself. This defers the definition of caching behavior to deployment time and allows easier control of the caching parameters and scope using the web.xml descriptor elements.

Best Practice  

Use the CacheFilter instead of wl:cache tags for page-level response caching whenever possible to provide better flexibility during deployment.

Note that a JSP page included using the jsp:include element is considered a separate page for the purposes of caching. Configuring the CacheFilter to cache the contents of the included JSP page represents a viable alternative to surrounding the jsp:include element with wl:cache tags.

Using Custom JSP Base Classes

Recall that JSP pages are processed by the JSP engine and converted to a servlet. In WebLogic Server, the resulting servlet is a subclass of weblogic.servlet .jsp.JspBase by default. JspBase is a WebLogic-provided class that extends HttpServlet and forwards service calls to a method called _ jspService() . You may create your own custom JSP base class that extends JspBase and configure the JSP engine to use your base class for generated servlets by including the extends attribute in the < %@ page ... % > directive on the JSP page.

The ability to define a custom JSP base class provides an alternative to static include directives for defining helper functions and utilities in the page. For example, if you want to provide a simple helper method called formatDate() to format a java.util.Date object, the method should probably be placed in a custom JSP base class rather than defining it in a separate file included using the < %@ include file= ... % > directive.

Using Run-Time Expressions in JSP Directives

Most of the attributes in JSP directives may be set using static information or using the contents of scriptlet expressions at run time. For example, the following simple jsp:include directive uses a statically defined target page:

 <jsp:include page=welcome.jsp /> 

The following version of the directive uses the value of the scriptlet variable mypage to determine which page to include at run time:

 <jsp:include page="<%= mypage %>" /> 

The syntax used in these dynamic expressions is very similar to the normal use of the < %= % > expression scriptlets used to generate content in the HTTP response with one notable exception: The parser cannot handle expressions with unescaped double quotes. For example, the following directive will fail during page parsing and compilation:

 <jsp:include page=<%= mypage + .jsp %> /> 

The expression should be written this way using escaped double quotes:

 <jsp:include page=<%= mypage + \.jsp\ %> /> 

If this becomes too confusing for complex expressions, simply create a temporary scriptlet variable before the directive and refer to that variable in the directive, as shown here:

 <% String fullname = mypage + .jsp; %> <jsp:include page=<%= fullname %> /> 

The example application built in Chapters 3 and 4 will make use of run-time expressions in jsp:include directives as part of its page-presentation architecture.

Creating Excel Files Using Servlets and JSP Pages

Creating spreadsheets using servlets and JSP pages is a useful way to provide users with results they can sort , manipulate, and print using Microsoft Excel or other spreadsheet applications. Servlets are the preferred mechanism, but JSP pages can also be used if you take steps to avoid unintended newline characters in the output stream.

To create a spreadsheet using a servlet, build the servlet in the normal manner but set the content type to application/vnd.ms-excel in the response header to indicate that the response should be interpreted as a spreadsheet. Data written to the response Writer object will be interpreted as spreadsheet data, with tabs indicating column divisions and newline characters indicating row divisions. For example, the SimpleExcelServlet servlet in Listing 1.8 creates a multiplication table using simple tabs and newlines to control the rows and columns in the result.

Listing 1.8:  SimpleExcelServlet.java.
start example
 package mastering.weblogic.ch01.example1; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SimpleExcelServlet extends HttpServlet  {     public static final String CONTENT_TYPE_EXCEL =          "application/vnd.ms-excel";     public void doGet(HttpServletRequest request,                        HttpServletResponse response)         throws IOException      {         PrintWriter out = response.getWriter();         response.setContentType(CONTENT_TYPE_EXCEL);         out.print("\t"); // empty cell in upper corner         for (int jj = 1; jj <= 10; jj++) {             out.print("" + jj + "\t");         }         out.print("\n");         for (int ii = 1; ii <= 10; ii++) {             out.print("" + ii + "\t");             for (int jj = 1; jj <= 10; jj++) {                 out.print("" + (ii * jj) + "\t");             }             out.print("\n");         }     } } 
end example
 

Normal registration of this servlet in web.xml is all that is required in most cases:

 <servlet>   <servlet-name>SimpleExcelServlet</servlet-name>   <servlet-class>     mastering.weblogic.ch01.example1.SimpleExcelServlet   </servlet-class> </servlet> <servlet-mapping>   <servlet-name>SimpleExcelServlet</servlet-name>   <url-pattern>/SimpleExcelServlet</url-pattern> </servlet-mapping> 

Users accessing the /SimpleExcelServlet location will be presented with a spreadsheet embedded in their browser window. The servlet may also be registered for a url-pattern that includes a .xls file extension to assist the user by providing a suitable default file name and type if they choose to use Save As... from within the browser:

 <servlet-mapping>   <servlet-name>SimpleExcelServlet</servlet-name>   <url-pattern>/multitable.xls</url-pattern> </servlet-mapping> 

Simple tab- and newline-based formatting may be sufficient in many cases, but you can achieve additional control by building HTML tables and using HTML formatting options such as < b > and < i > in the generated output. Because the content type was specified as ms-excel , these HTML tags are interpreted by the browser and spreadsheet application as equivalent spreadsheet formatting options.

The FancyExcelServlet example servlet in Listing 1.9 builds the same multiplication table as SimpleExcelServlet but uses HTML to control formats and cell sizes.

Listing 1.9:  FancyExcelServlet.java.
start example
 package mastering.weblogic.ch01.example1; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class FancyExcelServlet extends HttpServlet  {     public static final String CONTENT_TYPE_EXCEL =          "application/vnd.ms-excel";     public void doGet(HttpServletRequest request,                        HttpServletResponse response)         throws IOException      {         PrintWriter out = response.getWriter();         response.setContentType(CONTENT_TYPE_EXCEL);         out.print("<table border=1>");         out.print("<tr>");         out.print("<td>&nbsp;</td>"); // empty cell in upper corner         for (int jj = 1; jj <= 10; jj++) {             out.print("<td><b>" + jj + "</b></td>");         }         out.print("</tr>");         for (int ii = 1; ii <= 10; ii++) {             out.print("<tr>");             out.print("<td><b>" + ii + "</b></td>");             for (int jj = 1; jj <= 10; jj++) {                 out.print("<td>" + (ii * jj) + "</td>");             }             out.print("</tr>");         }         out.print("</table>");     } } 
end example
 

You can also use JSP pages to create spreadsheets with one complication: The output of a JSP page often contains many spurious newline characters caused by extra whitespace around directives and scriptlet tags, making it difficult to control the spreadsheet formatting when using simple tab and newline techniques. HTML formatting similar to the FancyExcelServlet works better in JSP pages used to create spreadsheets. Listing 1.10 presents the JSP equivalent to the FancyExcelServlet .

Listing 1.10:  FancyExcelPage.jsp.
start example
 <% response.setContentType("application/vnd.ms-excel"); %> <html> <body> <table border=1> <tr>   <td>&nbsp;</td>   <% for (int jj = 1; jj <= 10; jj++) { %>     <td><b><%= jj %></b></td>   <% } %> </tr> <% for (int ii = 1; ii <= 10; ii++) { %>   <tr>     <td><b><%= ii %></b></td>     <% for (int jj = 1; jj <= 10; jj++) { %>       <td><%= (ii * jj) %></td>     <% } %>   </tr> <% } %> </table> </body> </html> 
end example
 

Viewing Generated Servlet Code

Viewing the servlet code generated for a particular JSP page can be instructive while learning JSP technology and useful during the testing and debugging process. Often the error report received during the execution of the JSP page indicates the line in the generated servlet code, but finding the JSP scriptlet code or tag that caused the error requires inspection of the Java code.

Generated Java servlet code will be kept alongside the generated servlet class files if the keepgenerated parameter is set to true in the jsp-descriptor section of the weblogic.xml descriptor file. The equivalent option for keeping the generated Java code for JSP pages compiled using the weblogic.jspc utility is -keepgenerated placed on the command line used to execute weblogic.jspc .

By default, the generated servlet classes and Java code will be placed in a temporary directory structure located under the domain root directory. The name of this temporary directory depends on the names of the server, enterprise application, and Web application, and it typically looks something like /myserver/.wlnotdelete /_appsdir_masterapp_dir_webapp_myserver/jsp_servlet . This default location may be overridden using the workingDir option in the weblogic.xml descriptor file.

Programmatic Authentication in Web Applications

The J2EE specification provides a declarative mechanism for controlling access to resources in the Web application. This mechanism uses elements in the web.xml file to define collections of resources and specify the roles required to access these resources. Users are asked for their logon identifier and password via a pop-up HTTP challenge window or special form configured for this purpose; upon submission of valid credentials, the user will be authenticated in the Web application security context and will be able to access secured resources. Chapter 10 of this book will cover many different aspects of WebLogic Server security including this simple declarative security for Web applications.

This declarative mechanism can occasionally fall short of the desired functionality, and many developers have resorted to building their own authorization mechanisms. In many of these designs the users are never actually authenticated in the Web application security context, and access to business- tier resources like EJB components is accomplished through the default, or < anonymous >, user. This is a poor practice for at least two reasons:

  • Anyone with knowledge of the server listening port can connect to WebLogic Server as the < anonymous > user and gain access to the EJB components.

  • The Web application does not forward a useful security context to the EJB container, thereby eliminating the value of methods such as getCallerPrincipal() and isCallerInRole() in the EJB components for security and auditing purposes.

WebLogic Server provides a little-known interface to programmatically authenticate the user and establish a proper security context for this client in the Web application. The method is located in the weblogic.servlet.security.ServletAuthentication class and is called simply weak() . The example page shown in Listing 1.11 takes parameters passed in through the HttpServletRequest and authenticates the user using the weak() method.

Listing 1.11:  WeakLoginTest.jsp.
start example
 <%@ page import="weblogic.servlet.security.ServletAuthentication" %> <HTML> <HEAD> <TITLE>Login Tester JSP Page</TITLE> </HEAD> <BODY> <H2>User name before login is:    <%= weblogic.security.acl.Security.getThreadCurrentUserName() %> </H2> <%     // logs out if already logged in     ServletAuthentication.logout(request);      String username = request.getParameter("username");     String password = request.getParameter("password");  int retcode = ServletAuthentication.weak(username,   password, session);  if (retcode == ServletAuthentication.AUTHENTICATED) {         out.print("Successful login using " +                   username + " and " + password + ".<br>");     }     else {         out.print("Bad login/password.<br>");     } %> <H2>User name after login is:    <%= weblogic.security.acl.Security.getThreadCurrentUserName() %> </H2> </BODY> </HTML> 
end example
 

Subsequent page requests by the same client will continue to use this newly established security context, and any communication with the EJB container will pass this context information along and make it available via getCallerPrincipal() or other normal methods.




Mastering BEA WebLogic Server. Best Practices for Building and Deploying J2EE Applications
Mastering BEA WebLogic Server: Best Practices for Building and Deploying J2EE Applications
ISBN: 047128128X
EAN: 2147483647
Year: 2003
Pages: 125

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