| by Ronel Submicay
Most web developers at some point have
faced or will face the dilemma of writing web applications requiring
an initialization process triggered when a user first enters
as well as some type of housekeeping when a user leaves. With
the statelessness of the web, this task is not always straightforward
and is often quite messy without some type of session tracking
mechanism. At times developers may also need to operations when
a JSP application starts up or ends. In this article, we will
explore methods of triggering processes when JSP applications
are entered or exited by users.
What makes up our web application?
For the remainder of the article, JSPs containing the same application
name are considered to be within the same application. The name
of the application is retrieved using the getApplicationName
method of an instance of the ApplicationEventServlet. We will
create the ApplicationEventServlet in this article.
The ApplicationEventServlet will be an abstract class that
will perform operations triggered when an application or session
starts or ends. Any JSP extending the ApplicationEventServlet
will inherit the implemented actions. The life cycle of a named
application starts when the init method is called on the first
ApplicationEventServlet instance containing the name of the application
and ends when the destroy method is called on the first ApplicationEventServlet
instance containing the name of the application. For the above
cases, the doApplicationOnStart and doApplicationOnEnd methods
are called respectively.
The ApplicationEventServlet is also responsible for calling
the doSessionOnStart when a session starts and doSessionOnEnd
when a session expires, or is invalidated.
The following is our ApplicationEventServlet:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public abstract class ApplicationEventServlet extends HttpServlet
{
private static Hashtable s_AppEventCache =
null;
protected abstract String getApplicationName();
protected abstract boolean
doApplicationOnStart();
protected abstract boolean
doApplicationOnEnd();
protected abstract void
doSessionOnStart(HttpSession ses,
HttpServletRequest req,
HttpServletResponse res);
protected abstract void doSessionOnEnd(HttpSession ses);
public void init(ServletConfig config) throws
ServletException
{
super.init(config);
if( s_AppEventCache == null ) s_AppEventCache = new
Hashtable();
if( s_AppEventCache.get( getApplicationName() ) == null)
{
if ( doApplicationOnStart() ) {
s_AppEventCache.put( getApplicationName(), "" );
}
}
}
public void destroy()
{
super.destroy();
if( s_AppEventCache.get( getApplicationName() ) != null)
{
if( doApplicationOnEnd() )
{
s_AppEventCache.remove( getApplicationName() );
}
}
}
public void appSessionInit(HttpServletRequest req,
HttpServletResponse res)
throws IOException, ServletException
{
HttpSession ses = req.getSession(true);
if(ses.isNew()) {
doSessionOnStart(ses, req, res);
ses.putValue( getApplicationName(), new
SessionListener() );
}
}
class SessionListener implements HttpSessionBindingListener
{
public void valueBound(HttpSessionBindingEvent evt) {}
public void valueUnbound(HttpSessionBindingEvent evt) {
doSessionOnEnd(evt.getSession()); }
}
}
Lets now go through the ApplicationEventServlet's methods.
The first thing to note is that it is an abstract class. The
implementor is responsible for implementing the abstract methods:
getApplicationName
doApplicaionOnStart
doApplicationOnEnd
doSessionOnStart
doSessionOnEnd
Starting off with getApplicationName, this method returns
the String 'name' of the application represented by the concrete
class. This name will be used to tell whether an application
of the given name has already been called for doApplicationOnStart
or doApplicationOnEnd. The two methods are only called once per
named application per application life cycle.
The doApplicationOnStart is called only once for a named application
when an application is first started. So if myjsp1.jsp was requested
and it belonged to the application named Banking and doApplicationOnStart
has not already been called, then it will be called to initialize
the Banking application. If myjsp2.jsp also belonged to the Banking
application and was requested after myjsp1.jsp, then doApplicationOnStart
will not be called again since it has already been triggered
by myjsp1.jsp.
The doApplicationOnEnd is called only once for a named application
when an application ends. So if myjsp1.jsp was requested and
it belonged to the application named Banking and doApplicationOnEnd
has not already been called, then it will be called to perform
cleanup on the Banking application. If myjsp2.jsp also belonged
to the Banking application and was being destroyed after myjsp1.jsp,
then doApplicationOnEnd will not be called again since it has
already been triggered by myjsp1.jsp.
Synchronization of the two methods is not necessary, since
calls to init and destroy are already called synchronously.
The doSessionOnStart and doSessionOnEnd methods are only called
once for each new session, and when the session is removed, or
expires respectively.
Implemented methods in ApplicationEventServlet
Implemented methods in the ApplicationEventServlet are:
init
destroy
initSession
The init method determines when the applicationOnStart method
is called. It does this by keeping a Hashtable of named applications
that it has already called applicationOnStart for. The Hashtable
reference is s_AppEventCache. This Hashtable sticks around for
the whole life of the VM. The destroy method determines when
the applicationOnEnd method is called by checking into the s_AppEventCache
for the named application.
The initSession method is responsible for creating an HttpSessionBindingListener
and storing it in the user's session only if the session is new.
The inner class SessionListener will call into the sessionOnEnd
method will be called when the user's session expires or is invalidated.
Since the session is determined new within a request, the implementor
will have access to the request and response objects. This is
why we do not bother implementing the valueBound method, and
instead call doSessionOnStart directly to pass in the request
and response objects.
A Simple JSP Application Test Servlet
We will now implement the ApplicationEventServlet and apply it
to a couple of JSP pages. The following is our concrete class
called MyJSPApplicationServlet:
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import com.livesoftware.jrun.plugins.JSP.JSPResponse;
import com.livesoftware.jrun.plugins.JSP.JSPRequest;
public class MyJSPApplicationServlet extends ApplicationEventServlet
{
protected boolean doApplicationOnStart()
{
System.out.println("ApplicationOnStart called for:
"+getApplicationName());
return true;
}
protected boolean doApplicationOnEnd()
{
System.out.println("ApplicationOnEnd called for:
"+getApplicationName());
return true;
}
protected void doSessionOnStart(HttpSession ses,
HttpServletRequest req, HttpServletResponse res)
{
System.out.println("SessionOnStart called for:
"+getApplicationName());
System.out.println("Session id: "+ses.getId());
try {
res.getOutputStream().println("A new session
"+ses.getId());
}
catch(IOException io){ System.err.println("Bad
OutputStream");
}
protected void doSessionOnEnd(HttpSession ses)
{
System.out.println("SessionOnEnd called for:
"+getApplicationName());
System.out.println("Session id: "+ses.getId());
}
protected String getApplicationName()
{
return "MyJSPApplicationServlet";
}
}
This servlet defines a JSP page to extend MyJSPApplicationServlet.
It can give you an idea of when the methods are called and what
class wide logic you want to apply for any JSP page extending
MyJSPApplicationServlet.
The following is an example jsp file that uses the functionality
of MyJSPApplicationServlet.
JspAppTest.jsp
<%@ import=MyJSPApplicationServlet %>
<%@ extends=MyJSPApplicationServlet %>
<% appSessionInit (request, response); %>
MYJSPApplicationServlet test
The two directives import and extends is required within the
jsp file as well as the call to appSessionInit, which is responsible
for initializing the session. By creating two jsp files both
extending MyJSPApplicationServlet, you can get a clearer picture
of when the application and session methods are called.
What else can you do with it?
For starters, the ApplicationEventServlet can make available and manage objects
within the naming scope of the application. For instance, the s_AppEventCache
can be used to store name/Hashtable pairs where each Hashtable contains objects
that can be set and retrieved by appropriately named application. We can add
get and put methods so extending JSP pages can get to the available data, and
set them appropriately.
|