Programming Jakarta Struts, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Jakarta Struts, 2nd Edition [Electronic resources] - نسخه متنی

Chuck Cavaness

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








15.2 Using the Servlet Container for Logging


The Servlet specification requires that
every servlet container allow developers to log events to the
container log file. Although the name and location of the log file
are container-dependent, the manner in which these events are logged
is dictated by the specification and is portable across web
containers.

The
javax.servlet.ServletContext
class contains two methods that can be used for logging messages to
the container's log:

public void log( String msg );
public void log( String msg, Throwable throwable );

You can use either of these methods by obtaining the
ServletContext and passing the appropriate
arguments. Example 15-1 illustrates how this can be
done using a
Struts Action.


Example 15-1. The LoginAction using the ServletContext to log messages

public class LoginAction extends StorefrontBaseAction {
public ActionForward execute( ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response )
throws Exception{
// Get the user's login name and password. They should have already been
// validated by the ActionForm.
String email = ((LoginForm)form).getEmail( );
String password = ((LoginForm)form).getPassword( );
// Obtain the ServletContext
ServletContext context = getServlet( ).getServletContext( );
// Log which user is trying to enter the site
context.log( "Login email: " + email );
// Log in through the security service
IStorefrontService serviceImpl = getStorefrontService( );
UserView userView = serviceImpl.authenticate(email, password);
// Log the UserView for auditing purposes
context.log( userView.toString( ) );
UserContainer existingContainer = null;
HttpSession session = request.getSession(false);
if ( session != null ){
existingContainer = getUserContainer(request);
session.invalidate( );
}else{
existingContainer = new UserContainer( );
}
// Create a new session for the user
session = request.getSession(true);
existingContainer.setUserView(userView);
session.setAttribute(IConstants.USER_CONTAINER_KEY, existingContainer);
return mapping.findForward(IConstants.SUCCESS_KEY);
}
}

The LoginAction in Example 15-1
shows a very simple example of sending log messages to the
container's log file. It calls the log(
)
method and passes a literal string message that will be
written to the log. As was mentioned earlier, the name and location
of the log are dependent on which web container is being used.

Some web containers may assign a separate log file for each web
application, while others may use just a single log file. If only one
log file is used, messages from different web applications will end
up in the same log file and generally will be prefixed with the web
application name.


15.2.1 Using Filters


Filters
are a new feature of
the 2.3 Java Servlet specification. Servlet filters allow you to
inspect and/or transform the content of the HTTP request and response
objects. Because of the manner in which filters are invoked by the
servlet container, they can operate on dynamic as well as static
content.


Struts 1.1 supports both the 2.2 and 2.3 Servlet specifications. If
you are not using a 2.3-compliant container, you will not be able to
take advantage of servlet filters.

Using filters, servlet developers can perform the
following tasks:

  • Access a web resource before a request to it is invoked.

  • Process a request for a resource before it is invoked.

  • Modify the request headers and data by wrapping the request with a
    customized version of the request object.

  • Modify the response headers and data by wrapping the response with a
    customized version of the response object.

  • Intercept a method call on a resource after it has been performed.

  • Perform actions on a servlet, or group of servlets, by one or more
    filters in a specified order.


Based on the tasks required by servlet developers, the Servlet
specification describes several possible
types
of filters:

  • Authentication filters

  • Logging and auditing filters

  • Image conversion filters

  • Data compression filters

  • Encryption filters

  • Tokenizing filters

  • Filters that trigger resource-access events

  • XSLT filters that transform XML content

  • MIME-type chain filters

  • Filters that cache URLs and other information


All of these types of filters are interesting, but in this chapter we
are interested in using filters for logging and auditing. With
filters, it's possible to log messages using any
data that is contained in the request and response objects. Because
the filter is coupled tightly to the servlet container,
you'll probably still need logging functionality
elsewhere in your application. Using filters for logging is generally
not sufficient for the entire application. However, filters are
perfect for auditing or tracking a user's actions
through the system.

If you need to get an idea of which parts of your site your users are
frequenting or where certain user groups are going most often,
filters may be an ideal solution. They might even be able to give you
information about the specific data that the users are viewing most
often. For example, say that you have an online catalog application
and you are interested in knowing which catalogs the users are
browsing most often. Because the request and response objects contain
this information, you can easily track this information and store it
into a database for further analysis.


15.2.2 Creating a Filter


There
are three basic steps for creating a filter:

  1. Create a Java class that implements the
    javax.servlet.Filter interface and that contains a
    no-argument constructor.

  2. Declare the filter in the web application deployment descriptor using
    the filter element.

  3. Package the filter class along with the rest of the web application
    resources.



15.2.2.1 Creating the filter class

The first step in creating a servlet filter is to create a new Java
class (or use an existing one) and have it implement the
javax.servlet.Filter
interface. Java classes can implement multiple interfaces, so you
don't necessarily have to create a new class.
However, the class will eventually need to be loaded by the web
container, so it shouldn't be one that is installed
only on a backend system, like an EJB container.

The Filter interface has three methods that must
be implemented by your class:

public void init(FilterConfig filterConfig) throws ServletException;
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException;
public void destroy( );

The web container calls the init() method when
it's ready to put the filter into service. You can
initialize any needed resources in this method. The destroy(
)
method is the opposite of the init()
method. The web container calls this method when it takes the filter
out of service. At this point, you should clean up any open resources
that the filter may be using, such as database connections.

Finally, the web container calls the doFilter()
method every time a request is received and the container determines
that the filter instance should be notified. This is where you should
place whatever functionality the filter is designed to perform.

Example 15-2 shows an
example filter class that could be used
to log to the servlet log file or to initialize a third-party logging
service.


Example 15-2. A servlet filter class example

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletContext;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
/**
* An example servlet filter class.
*/
public class LoggingFilter implements Filter{
public final static String LOG_FILE_PARAM = "log_file_name";
private FilterConfig filterConfig = null;
private ServletContext servletContext = null;
public void init( FilterConfig config ) throws ServletException {
// Initialize any neccessary resources here
this.filterConfig = config;
this.servletContext = config.getServletContext( );
// You can get access to initialization parameters from web.xml
// although this example doesn't really use it
String logFileName = config.getInitParameter( LOG_FILE_PARAM );
// You can log messages to the servlet log like this
log( "Logging to file " + logFileName );
// Maybe initialize a third-party logging framework like log4j
}
public void doFilter( ServletRequest request,
ServletResponse response,
FilterChain filterChain )
throws IOException, ServletException {
// Log a message here using the request data
log( "doFilter called on LoggingFilter" );
// All request and response headers are available to the filter
log( "Request received from " + request.getRemoteHost( ) );
// Call the next filter in the chain
filterChain.doFilter( request, response );
}
public void destroy( ){
// Remove any resources to the logging framework here
log( "LoggingFilter destroyed" );
}
protected void log( String message ) {
getServletContext( ).log("LoggingFilter: " + message );
}
protected ServletContext getServletContext( ){
return this.servletContext;
}
}


Just as you must be careful with multiple threads in Java servlets,
you must be careful not to violate any thread-safety practices with
filters. The servlet container may send concurrent threads to a
single instance of a filter class, and you must ensure that you
don't do anything to cause problems between the
threads. In other words, no client-specific data should be stored in
instance variables. Local variables are fine, just as they are in
Java servlets, because they are stored on the stack rather than the
heap.


15.2.2.2 Declaring the filter in the deployment descriptor

The second step in creating a servlet filter is to configure the
proper elements in the deployment
descriptor for the web application. As you learned in Chapter 4, the name of the deployment descriptor file
for a web application is web.xml.

The first step in setting up the filter declaration in the web
application's deployment descriptor is to create the
actual filter elements.
Chapter 4 describes the
filter element in detail. The following deployment
descriptor fragment illustrates how it looks using the
LoggingFilter class from the
previous section:

<filter>
<filter-name>MyLoggingFilter</filter-name>
<filter-class>LoggingFilter</filter-class>
<init-param>
<param-name>log_file_name</param-name>
<param-value>log.out</param-value>
</init-param>
</filter>

You can also optionally specify initialization parameters, icons, a
description, and a display label. See Chapter 4
for more details on the attributes of the filter
element.

Once the filter element is added, you need to add
a filter-mapping
element that will associate or link the specified filter to a servlet
or static resource in the web application. Filters can be applied to
a single servlet or to groups of servlets and static content, using
two distinct mapping approaches. The following deployment descriptor
fragment illustrates a
filter mapping to a single servlet
called MyExampleServlet:

<filter-mapping>
<filter-name>MyLoggingFilter</filter-name>
<servlet-name>MyExampleServlet</servlet-name>
</filter-mapping>

Every time the web container receives a request for the
MyExampleServlet resource, the doFilter(
)
method in the LoggingFilter class will
be invoked. The following XML fragment illustrates how the example
filter can be mapped to all requests sent to the web application:

<filter-mapping>
<filter-name>MyLoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

The filter mapping in this case will map all requests to the
MyLoggingFilter because every request URI will
match the "/*" URL pattern.


15.2.2.3 Packaging the filter

The final step is to
package
the filter class with the rest of the resources of the web
application. As with any other Java resource that is part of a web
application, the filter class must be bundled with the WAR file and
able to be loaded by the web application's class
loader. In most cases, the filter class should be placed under the
WEB- INF/classes directory for the web
application. Filter classes may also be inserted into a JAR file and
placed in the WEB-INF/lib directory.


15.2.3 Using Event Listeners


Web application event listeners are
Java classes that implement one or more of the servlet event-listener
interfaces. Event listeners support event notifications for changes
in state in the ServletContext and
HttpSession objects. Event listeners that are
bound to the ServletContext support changes at the
application level, while those that are bound to the
HttpSession objects are notified for state changes
at the session level.

Multiple listeners can be set up for each event type, and the servlet
developer may offer a preference regarding the notification order for
the listeners based on event type.

Tables Table 15-1 and Table 15-2
list the event types and event-listener interfaces available to the
servlet developer.

Table 15-1. ServletContext application events and listener interfaces

Event type


Description


Listener interface


Lifecycle


The ServletContext is about to service the first
request or is about to be shut down by the servlet container.


ServletContextListener


Attributes


Attributes on the ServletContext have been added,
removed, or replaced.


ServletContextAttributesListener

Table 15-2. HttpSession application events and listener interfaces

Event type


Description


Listener interface


Lifecycle


An HttpSession object has been created,
invalidated, or timed out.


HttpSessionListener


Attributes


Attributes on an HttpSession object have been
added, removed, or replaced.


HttpSessionAttributesListener


15.2.4 Creating an Event Listener


The
steps for creating an event listener are similar to those of creating
filters. There are three primary steps to perform:

  1. Create a Java class that implements the event-listener interface for
    which you are interested in receiving events. This class must contain
    a no-argument constructor.

  2. Declare the event listener in the web application deployment
    descriptor using the listener element.

  3. Package the event listener class along with the rest of the web
    application resources.



15.2.4.1 Creating the event listener class

As when you create filters, the first step is to create a Java class
that implements the appropriate listener interface. As an example,
we'll create a Java class that implements the
javax.servlet.ServletContextListener interface and
is responsible for initializing the logging service when the web
application is started. This class is illustrated in Example 15-3.


Example 15-3. ServletContextListener interface

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* An example event listener class that
* initializes a logging service.
*/
public class LoggingListener implements ServletContextListener{
private ServletContext context = null;
/**
* Called by the container before the first request is
* processed. This is a good time to initialize
* the logging service.
*/
public void contextInitialized( ServletContextEvent event ){
this.context = event.getServletContext( );
// Initialize the logging service here
// Log a message that the listener has started
log( "LoggingListener initialized" );
}
/**
* Called by the container when the ServletContext is about
* ready to be removed. This is a good time to clean up
* any open resources.
*/
public void contextDestroyed( ServletContextEvent event ){
// Clean up the logging service here
// Log a message that the LoggingListener has been stopped
log( "LoggingListener destroyed" );
}
/**
* Log a message to the servlet context application log or
* system out if the ServletContext is unavailable.
*/
protected void log( String message ) {
if (context != null){
context.log("LoggingListener: " + message );
}else{
System.out.println("LoggingListener: " + message);
}
}
}

The event-listener class in Example 15-3 contains two
methods that are invoked by the web container:

public void contextInitialized( ServletContextEvent event );
public void contextDestroyed( ServletContextEvent event );

The web container calls the contextInitialized()
method before the first request is processed. You should initialize
any needed resources in this method. For example, this is an ideal
location to initialize a logging service. The
contextDestroyed() method is called when the web
application is taken out of service. This is where any open resources
that the listener class is using should be closed.

Because there can be multiple event-listener classes for the same
event, let's use another
ServletContext event-listener class to make our
example more realistic. Example 15-4 shows the
DBConnectionPoolListener
class. Both the LoggingListener and the
DBConnectionPoolListener will receive event
notifications when the ServletContext is
initialized and destroyed.


Example 15-4. The DBConnectionPoolListener class

import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
/**
* An example event listener class that
* initializes the database connection pooling.
*/
public class DBConnectionPoolListener implements ServletContextListener{
/**
* Called by the container before the first request is
* processed. This is a good time to initialize
* the connection pooling service.
*/
public void contextInitialized( ServletContextEvent event ){
// Initialize the connection pooling here
}
/**
* Called by the container when the ServletContext is about
* ready to be removed. This is a good time to clean up
* any open resources.
*/
public void contextDestroyed( ServletContextEvent event ){
// Shut down the connection pooling and open database connections
}
}

Because both the LoggingListener and
DBConnectionPoolListener are listening for the
same type of application events, the servlet container will notify
them in the order in which they are listed in the descriptor, based
on the event type.


15.2.4.2 Declaring the event listener in the deployment descriptor

The following web application deployment descriptor fragment shows
you how to set up the event listeners:

<web-app>
<listener>
<listener-class>LoggingListener</listener-class>
</listener>
<listener>
<listener-class>DBConnectionPoolListener</listener-class>
</listener>
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>ExampleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/example</url-pattern>
</servlet-mapping>
</web-app>

The LoggingListener will be notified first,
followed by the DBConnectionPoolListener instance.
When the web application shuts down, the listeners are notified in
reverse order. The HttpSession event listeners are
notified prior to listeners for the application context.


15.2.4.3 Packaging the event listener

The
packaging of the event-listener classes follows the guidelines
described in the previous section for filters. That is, the classes
must either be in the WEB-INF/classes directory
or be installed in a JAR file located in the
WEB-INF/lib directory for the web application.


    / 181