Oracle JSP - Developers Guide
Release 1.0


Prev Next

4
Developing Web Applications

JSP is a technology for building a dynamic server page. Typically, pages are grouped together to provide a specific function or application. In some web server environments, the purpose of the server is to present this single function. That is, the server is the application (or vice versa). In other cases, the server hosts many applications. In this latter environment, developers need a model where their application development choices are isolated from the other applications running in the server. For JSPs, a rudimentary model is provided through inheritence from the Java servlet model it runs on. Oracle JSP extends beyond this by providing more complete support for the application developer.

Samples that illustrate the concepts discussed in this chapter are provided in the chapter 4 samples section included in the download.

4.1 Application Needs

HTTP supports two styles of applications: stateful and stateless. Stateful applications run in a server environment where the application's state is maintained on a per client basis between requests. This is called a session-based application. Such applications typically acquire or release resources at session boundaries. Resources are acquired when a session starts and released when a session completes. To do this an application must have a mechanism for being notified when these boundaries occur.

A stateless application relies on no such facility. However, though they may not use server sessions, few applications can be truly stateless. Most stateless applications rely on memory caches to increase performance. They differ from stateful applications in that the application's real state is maintained in a persistent store; thus, the application can recover its cache if necessary. This allows a greater flexibility in your choice of server types and configurations that you can run on to increase scalability and performance. However, in this type of stateless application, there is still an issue of when to clear the memory in the cache. In a server environment supporting many applications, the cache should logically be emptied when the application is terminated, not when the server is shut down. To do this, an application must have a mechanism for being notified when the application boundaries occur such as when an application starts and more importantly when it is shut down.

4.2 Servlet Applications

The JSP 1.0 Specifications rely on the underlying Servlet 2.1 engine to support its application needs. Servlet 2.1 provides the following support for applications:

4.3 Oracle JSP Applications

The Oracle JSP implementations attempt to overcome the limitations of the servlet environment to provide complete application support in the following ways:

Oracle JSP provides the developer with the ability to write code to handle the session and application lifecycle transitions within the JSP environment. Oracle JSP defines four lifecycle events: sessionOnStart, sessionOnEnd, applicationOnStart, and applicationOnEnd. The JSP developer scripts the handlers to these events in the globals.jsa file.

Additionally, for the Java object developer, Oracle JSP defines a new event listener, JspScopeListener. A JspScopeListener is a Java object that is designed to run in a JSP environment. Oracle JSP notifies listeners with a JspScopeEvent when the object is going out of JSP scope.

Objects are attached to a JSP scope using tags like <jsp:useBean>. There are four scopes: page, request, session, and application. When the end of a scope is reached, Oracle JSP notifies all the objects that have been attached to that scope (and are JspScopeListeners) that they are going out of scope.

This mechanism provides the greatest benefit for page developers that need to clear object resources at the page or request level, regardless of whether or not errors occur during the execution of the page. By writing JspScopeListeners, page developers do not have to surround their page implementations in Java try/catch/finally blocks.

4.4 globals.jsa Syntax

In Oracle JSP, the globals.jsa has two functions. First, it is an application marker. Its existence defines the root of the JSP application. Second, it contains the application's event handlers and additional global declarations.

Note: It is possible (and common) to only use the globals.jsa as an application marker. That is it is valid that the globals.jsa be a zero byte or empty file.

4.4.1 Case-sensitivity

JSPs run on many platforms. Some platforms like Sun Solaris have case-sensitive file repositories. Oracle JSP expects the name of the globals.jsa file to be completely lowercase. Though mixed case will work in non-case sensitive environments, you should avoid mixing case to avoid difficult-to-diagnose problems in case-sensitive environments.

Note (for those interested in debugging a JSP in Java debugger): Although the complete globals.jsa filename must be lowercase, the generated class name follows the Java convention of uppercasing the first character. Specifically, a globals.jsa file generates a class named Globals.

4.4.2 Event Handlers

JSPs define four scopes: application, session, request, and page. Application developers often need to initialize a part of their environment when a scope begins and they must clean up this environment when it ends. To address these needs, programming environments define events that correspond to the above lifecycle transitions. Application developers write event handlers for the pertinent events.

Oracle JSP provides two forms of lifecycle event notification: one for the JSP developer and one for the Java (object) developer providing the business logic incorporated into the JSPs. This section discusses the former, while the next section, JspScopeListener, discusses the latter.

As the start and end of the request and page scopes are by and large equivalent to the JSP page itself, Oracle JSP only defines JSP events for the application and session lifecycles. The JspScopeListener section that follows contains an example of defining your own lifecycle events for the request and page and dispatching these events to handlers written in the JSP page.

An Oracle JSP developer has the option of writing event handlers for whenever the application or the session begins or terminates. These event handlers are written in globals.jsa. Unlike a regular JSP page, where the text and code within the page defines a single block of code, globals.jsa supports four distinct blocks of code. There is a block of code for each event handler. To distinguish between these blocks of code, each event handler is written in the body of an event tag that defines the specific handler being implemented. The four event tags are:

<event:application_OnStart>
<event:application_OnEnd>
<event:session_OnStart>
<event:session_onEnd>

The following is a simple globals.jsa sample file that counts the number of page hits in the application while the application is alive as well as tracks the number of active sessions:

Note: This globals.jsa simplifies the sample below by using the ability to globally declare beans. This globals.jsa is written using the jml tags.

globals.jsa sample

<event:application_OnStart>

	<%-- Initializes counts to zero --%>

	<jsp:useBean id="pageCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />
    <jsp:useBean id="sessionCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />
    <jsp:useBean id="activeSessions" class="oracle.jsp.jml.JmlNumber" scope = "application" />

	<%-- Consider storing pageCount persistently -- If you do read it here --%>

</event:application_OnStart>

			

<event:application_OnEnd>

    <%-- Acquire beans --%>
    <jsp:useBean id="pageCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />
    <jsp:useBean id="sessionCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />

	<% application.log("The number of page hits were: " + pageCount.getValue() ); %>
    <% application.log("The number of client sessions were: " + sessionCount.getValue() ); %>

	<%-- Consider storing pageCount persistently -- If you do write it here --%>

</event:application_OnEnd>

			
<event:session_OnStart>

	<%-- Acquire beans --%>
    <jsp:useBean id="sessionCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />
    <jsp:useBean id="activeSessions" class="oracle.jsp.jml.JmlNumber" scope = "application" />

    <%
    sessionCount.setValue(sessionCount.getValue() + 1);
    activeSessions.setValue(activeSessions.getValue() + 1);
    %>

	<br>
    Starting session #: <%=sessionCount.getValue() %> <br>
    There are currently <b> <%= activeSessions.getValue() %> </b> active sessions <p>

</event:session_OnStart>

			
<event:session_OnEnd>

	<%-- Acquire beans --%>
    <jsp:useBean id="activeSessions" class="oracle.jsp.jml.JmlNumber" scope = "application" />

	<%
    activeSessions.setValue(activeSessions.getValue() - 1);
    %>

</event:session_OnEnd>
		

4.4.2.1 Event Tag syntax

Each event tag has identical syntax: the event start tag, the body of the event handler, and the event end tag. For example:

<event:session_OnStart>
<% This scriptlet contains the implementation of the event handler %>
</event:session_OnStart>

4.4.2.2 Event Body syntax

The body of the an event tag can contain any valid jsp tag including tag library tags such as those defined by JML. The scope of such tags is however limited to the event tag's body. Thus in the example above, beans declared using the <jsp:useBean> tag must be redeclared in each event handler in which they are used. This can be avoided if beans are globally declared. For more information, see section 4.4.3.2 Global declarations.

Static text as used in a regular JSP page can only reside in <event:session_OnStart>. The other event handlers can only contain code. See descriptions below for further details.

4.4.2.3 Predeclared (implicit) Event objects

Like a regular JSP page, Oracle JSP predefines the pertinent runtime objects in each event handler. These objects can be referenced by the developer without further declaration. The objects available to an event handler are not uniform. The specific objects available to a given event handler are described in the following sections.

4.4.2.4 <event:application_OnStart>

The body of the <event:application_OnStart> tag is executed when the Oracle JSP loads the first JSP page in this application. This usually occurs when the first HTTP request is made to any page in the application from any client. Applications use this event to initialize application-wide resources. For example, one might create a database connection pool or read data from a persistent repository into application objects.

The event handler cannot contain static text. It can only contain jsp tags (or tag library tags such as JML) and white space. The implicit object for <event:application_OnStart> is application. All other page level objects, session, request, response, and out are not available.

Errors that occur in this event handler and are not dealt with in code are automatically trapped by the JSP runtime and logged using the servletContext (application). Processing then proceeds as if no error had occurred.

The following sample is used in the extended Lottery sample. In this sample, the generated lottery numbers for a particular user are cached for an entire day. If the user re-requests their picks they get the same set of numbers. The cache is recycled once a day giving each user a new set of picks. To properly implement, the Lottery application must persist the cache when the application is being shutdown and refresh the cache when the application is reactivated. This code fragment is the application_OnStart handler that reads the cache from a file named "lotto.che".

Lottery application_OnStart handler sample file

<event:application_OnStart>

	<%
    Calendar today = Calendar.getInstance();

    // Store current date at application scope so know when to invalidate cache
    application.setAttribute("today", today);

    // persistent information written into file relative to application root
    try {
        FileInputStream fis = new FileInputStream(
            application.getRealPath("/") + File.separator+"lotto.che");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Calendar cacheDay = (Calendar) ois.readObject();

            // Only re-establish from cache if cache is from today
            if (cacheDay.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) {
                cachedNumbers = (Hashtable) ois.readObject();
                // variables defined via <jsp:useBean> are attached with "beans/" prefix
                application.setAttribute("beans/cachedNumbers", cachedNumbers); 
            }
            ois.close();
        } catch (Exception theE) {
            // catch all -- can't use persistent data
        }
    %>

</event:application_OnStart>
		

4.4.2.5 <event:application_OnEnd>

The body of the <event:application_OnStart> tag is executed when Oracle JSP unloads the JSP application. This currently occurs whenever a previously loaded page is reloaded after dynamic translation or when the Oracle JSP servlet is itself terminated (its destroy method is called) by the underlying servlet environment. Applications use this event to cleanup application level resources or to write application state to a persistent store.

The event handler cannot contain static text. It can only contain jsp tags (or tag library tags such as JML) and white space. The implicit object for <event:application_OnEnd> is application. All other page-level objects, session, request, response, and out are not available.

Errors that occur in this event handler and are not dealt with in code are automatically trapped by the JSP runtime and logged using the servletContext (application). Processing then proceeds as if no error had occurred.

The following sample is the corresponding application_OnEnd handler for the Lottery sample described above. In this handler, the cache is written to a file before the application is terminated.

Lottery application_OnEnd handler sample

<event:application_OnEnd>

	<%
    Calendar now = Calendar.getInstance();
    Calendar today = (Calendar) application.getAttribute("today");

    // delete existing file if this cache is no longer valid because 
    // its a new day.

    if (cachedNumbers.isEmpty() || 
        now.get(Calendar.DAY_OF_YEAR) > today.get(Calendar.DAY_OF_YEAR)) {
        File f = new File(application.getRealPath("/")+File.separator+"lotto.che");
        if (f.exists()) f.delete();
        return; 
    }

    try {

        FileOutputStream fos = 
            new FileOutputStream(application.getRealPath("/")+File.separator+"lotto.che");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(today);
        oos.writeObject(cachedNumbers);
        oos.close();
    } catch (Exception theE) {
        // catch all -- can't use persistent data
    }
    %>

</event:application_OnEnd>
		

4.4.2.6 <event:session_OnStart>

The body of the <event:session_OnStart> tag is executed when Oracle JSP creates a new session in response to a JSP page request. This occurs on a per client basis whenever the first request is received for a session-enabled JSP page in an application. Applications use this event to initialize per client resources. Additionally, applications use this event to control where a client starts in an application. The following code makes sure that client starts a new session on the application's initial page.

<event:session_OnStart>

<% if (!page.equals("index.jsp")) { %>
	<jsp:forward page="index.jsp" />
<% } %>

</event:session_OnStart>
		

Note: Oracle JSP implements the <jsp:forward> tag so that its use in the session_OnStart handler has the same effect as when its used within a page. Specifically, calling <jsp:forward> in session_onStart will cause the page the current request that instigated the session to return directly from the forward without any page processing. This is done by having the forward implementation set the request attribute "oracle.jsp.requestRedirected". Any value is valid as the runtime uses the existence of this attribute as an indication further processing should not be done. If you use other techniques to redirect to a different page from within the session_OnStart handler, you must explicitly set this attribute to prevent the originating page from being processed. For example if you call response.sendRedirect() or requestDisptacher.forward().

All page level objects, application, session, request, response, and out are available in this handler. An additional object page is also available. For example, page is a string containing the name of the requested page that caused the session to be activated. Because the out object is available, this is the only event handler that can contain static text in addition to jsp tags. Ouput from the session_OnStart handler is written before the page's ouput in the response stream.

Note: As session_OnStart is called before a page's code is executed, the output from a session_OnStart handler appears before any output from the page. The session_OnStart handler and the page which triggered this event share the same out stream. The bufferSize of this stream is controlled by the bufferSize of the page. The session_OnStart handler doesn't automatically flush the stream. It is only flushed according to the general JSP rules (non-buffered stream or buffer overflows). This means headers can still be written in pages that trigger the session_OnStart event.

Errors that occur in this event handler and are not dealt with in code are automatically trapped by the JSP runtime and logged using the servletContext (application). Processing then proceeds as if no error had occurred.

4.4.2.7 <event:session_OnEnd>

The body of the <event:session_OnEnd> tag is executed when Oracle JSP invalidates an existing session. This occurs when either the application itself invalidates the session by calling session.invalidate() or the session just expires (times-out) on the server. Applications use this event to release client resources.

<event:session_OnEnd>

    <%-- Acquire beans --%>
	<jsp:useBean id="activeSessions" class="oracle.jsp.jml.JmlNumber" scope = "application" />
	
    <%
	activeSessions.setValue(activeSessions.getValue() - 1);
    %>

</event:session_OnEnd>
		

Of the page level objects, only application and session are available in this handler. The object's request, response, and out are not available. The additional object page is also not available in this handler.

Errors that occur in this event handler and are not dealt with in code are automatically trapped by the JSP runtime and logged using the servletContext (application). Thus, processing proceeds as if no error had occurred.

4.4.3 Global declarations

In addition to holding event handlers, globals.jsa is used to globally declare directives and objects for the JSP application. Outside the boundaries of the event tags, the globals.jsa cannot use static text, JSP scriplets (<% %>) or JSP expressions except when used within an attribute of a valid tag (<%= %>). You can include any JSP directives, JSP declarations (<%! %>), JSP comment (<%-- --%>), and any tag that that has a scope attribute (<jsp:useBean>, <jml:useVariable>, etc.).

4.4.3.1 Global directives

Directives used within a globals.jsa provide a dual purpose. First, they declare the information that is required to process the globals.jsa. Just as importantly, they serve as default values for succeeding pages. Specifically, any directive used in globals.jsa implicitly becomes a directive used in all JSP pages in this application, unless explicitly overwritten by the page.

A directive is overwritten on an attribute-by-attribute basis. Thus, if a globals.jsa has a <%@ page %> directive, and a subsequent application page also has a <%@ page %> directive, then the JSP page's resulting page directive is the union of the two directives with precedence given to the page. For example:

globals.jsa directive: <%@ page import="java.util.*" bufferSize="10kb" %>
page directive: <%@page bufferSize="20kb" %>

is the equivalent of: <%@ page import="java.util.*" bufferSize="20kb" %>

The following example defines the JML tag library for the globals.jsa as well as for all subsequent pages. By including the <%@ taglib > directive in the globals.jsa, it does not have to be included in any of this application's pages.

<%-- Directives at the top --%>

<%@ taglib uri="oracle.jsp.parse.OpenJspRegisterLib" prefix="jml" %>

<%-- Declare global objects here --%>

<%-- Application lifecycle event handlers go here --%>
<%-- Declarations used by the event handlers go here --%>
		

4.4.3.2 Global declarations

In addition to directives, you can also use the JSP declaration (<%! %>) tag in a globals.jsa. Code within the body of the directive tag declares and implements the additional methods and data members that the globals object supports. JSP pages do not have access to the globals object. Therefore, these declarations cannot be used to implement an application library. Declaration support is provided in globals.jsa so that common functions shared across event handlers can be written and shared.

4.4.3.3 Global beans

Probably the most common element declared in globals.jsa are (global) objects. Objects declared in globals.jsa are made part of the implicit object environment of all the application's pages as well as the globals.jsa event handlers. Specifically, an object declared in globals.jsa is not redeclared (<jsp:useBean>) in each of the application pages that reference it. You declare a global object using any jsp tag or extension that has a scope attribute (<jsp:useBean>, <jml:useVariable>, and so on.). Globally declared objects must either be of session or application scope. You cannot globally declare an object of request or page scope.

Note: Nested tags are supported. Thus, <jsp:setProperty> can be used to initialize a bean if nested in the <jsp:useBean>. A translation error occurs if <jsp:setProperty> is used outside a <jsp:useBean>.

When a global object is used in a globals.jsa event handler, the position of its declaration is important. Only those objects that are declared before the event handler in the globals.jsa are added as implicit objects to the handler. For this reason, developers are advised to structure their globals.jsa as follows:

global directives
global objects
event handlers
globals declarations

The following example declares three application variables for use by all pages:

<%-- Directives at the top --%>
<%-- Declare global objects here --%>
    <%-- Initializes counts to zero --%>
	<jsp:useBean id="pageCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />
    <jsp:useBean id="sessionCount" class="oracle.jsp.jml.JmlNumber" scope = "application" />
    <jsp:useBean id="activeSessions" class="oracle.jsp.jml.JmlNumber" scope = "application" />

<%-- Application lifcycle event handlers go here --%>
<%-- Declarations used by the event handlers go here --%>
		

4.5 JspScopeListener

The globals.jsa event handlers provide the JSP developer a mechanism to respond to JSP application lifecycle events. However, because the JSP architecture encourages encapsulating most logic in beans to simplify the page, Oracle JSP provides an additional mechanism for the JSP bean developer to respond to lifecycle events. As more beans are implemented to manage themselves within a JSP application, the need for writing globals.jsa event handlers will be reduced.

Java beans use the Java EventListener model to partake in event notification. Oracle JSP defines a new event listener class, JspScopeListener, that allows objects to receive a JspScopeEvent. Oracle JSP sends this event to all objects implementing the JspScopeListener interface that are attached to a particular JSP scope that is completing.

For example, if a JSP page has used <jsp:usebean> to declare an object whose scope is request, then Oracle JSP will notify the object's event listener (if it has one) when the request is completing. This allows the object to reduce its resources until the next request arrives.

An interesting aspect of this is that by locating the cleanup in the object (listener) itself, scripting in the JSP is simplified. For the situation described above, where a request scope object is notified after the request completes, the benefit for the JSP developer is that special code does not have to be written to handle the cleanup whether or not the request is completes successfully or via an error.

The following sample implements a JspScopeListener that redispatches the events to the JSP page giving the effect that the page supports a request_OnEnd and page_OnEnd event handler. The complete source for this sample is included in the JSP samples download.

Code Fragment from PageEventDispatcher.java

public class PageEventDispatcher extends Object implements JspScopeListener {

    public void setPage(Object page) {
        this.page = page;
    }

    public void setMethodName(String m) throws NoSuchMethodException, ClassNotFoundException  {
        method = verifyMethod(m);
        methodName = m;
    }

    public void outOfScope(JspScopeEvent ae) {
        int scope = ae.getScope();

        if ((scope == javax.servlet.jsp.PageContext.REQUEST_SCOPE  || 
             scope == javax.servlet.jsp.PageContext.PAGE_SCOPE) && method != null) {
            try {
                Object args[] = {ae.getApplication(), ae.getContainer()};
                method.invoke(page, args);
            } catch (Exception e) {
                // catch all and continue
            }
        }
    }
}
		

PageEventDispatcher is a Bean that implements the JspScopeListener interface. The interface defines a single event method, outOfScope. The listener's outOfScope method is called when the scope that the object is attached to is ending. The event contains the scope that is ending, the container object that is the repository for objects at this scope, the name of the object that the notification pertains to, and the JSP application object.

In this sample, thePageEventDispatcher acts as a redispatcher for the JSP page allowing the JSP page to host request_OnEnd and page_OnEnd event handlers. The JSP page creates a PageEventDispatcher for each scope for which it wants to provide an event handler. It then registers the event handler method in itself with the PageEventDispatcher object. When the PageEventDispatcher object is notified that it is going out of scope, it turns around and calls the page's registered OnEnd method.

Declaring the PageEventDispatcher in a JSP page

<%-- declare request and page scoped beans here --%>

<%-- declare the event dispatchers --%>
<jsp:useBean id = "requestDispatcher" class = "oracle.jsp.sample.event.PageEventDispatcher" scope = "request" >
	<jsp:setProperty name = "requestDispatcher" property = "page" value = "<%= this %>" />
	<jsp:setProperty name = "requestDispatcher" property = "methodName" value = "request_OnEnd" />
</jsp:useBean>

<jsp:useBean id = "pageDispatcher" class = "oracle.jsp.sample.event.PageEventDispatcher" scope = "page" >
	<jsp:setProperty name = "pageDispatcher" property = "page" value = "<%= this %>" />
	<jsp:setProperty name = "pageDispatcher" property = "methodName" value = "page_OnEnd" />
</jsp:useBean>

The request event handler logs the number of chained included pages handled in this request. The page event handler logs the page count. In the case of chained included pages this should output once per (included) page.

JSP Page Event Handlers


<%! 
	// request_OnEnd Event Handler
	public void request_OnEnd(ServletContext application, HttpServletRequest request) {
		// acquire beans
		oracle.jsp.jml.JmlNumber includeCount = 
		    (oracle.jsp.jml.JmlNumber) request.getAttribute("includeCount");

		// now cleanup the bean
		if (includeCount != null)
             application.log("request_OnEnd: Include count = " + includeCount.getValue());	
	}

	// page_OnEnd Event Handler
	public void page_OnEnd(ServletContext application, PageContext page) {
		// acquire beans
		oracle.jsp.jml.JmlNumber pageCount = 
		    (oracle.jsp.jml.JmlNumber) page.getAttribute("pageCount");

		// now cleanup the bean -- uncomment code for real bean
		if (pageCount != null) application.log("page_OnEnd: Page count = " + pageCount.getValue());
	}  
%>

<%-- Page implementation goes here --%>

<jsp:setProperty name = "includeCount" property = "value"
    value = '<%= (request.getAttribute(
        "javax.servlet.include.request_uri") != null) ? includeCount.getValue() + 1 : 0 %>' /> 

<h2> Hello World </h2>

Included: <%= request.getAttribute("javax.servlet.include.request_uri") %>   
Count: <%= includeCount.getValue() %> <br>

<% if (includeCount.getValue() < 5) { %>
	<jsp:include page="scope.jsp" flush = "true" />
<% } %>
	

To drive this sample, the page includes itself up to five times.

Page implementation




			
<%-- Page implementation goes here --%>

<jsp:setProperty name = "includeCount" property = "value"
    value = '<%= (request.getAttribute(
        "javax.servlet.include.request_uri") != null) ? includeCount.getValue() + 1 : 0 %>' /> 

<h2> Hello World </h2>

Included: <%= request.getAttribute("javax.servlet.include.request_uri") %>   
Count: <%= includeCount.getValue() %> <br>

<% if (includeCount.getValue() < 5) { %>
	<jsp:include page="scope.jsp" flush = "true" />
<% } %>
	


Prev

Next

Oracle
Copyright © 1999 Oracle Corporation.

All Rights Reserved.


Contents


Index