What if you want an app init parameter that’s a database DataSource?

image with no caption

Context parameters can’t be anything except Strings. After all, you can’t very well stuff Dog object into an XML deployment descriptor. (Actually, you could represent a serialized object in XML, but there’s no facility for this in the Servlet spec today... maybe in the future.)

What if you really want all the parts of your web app to have access to a shared database connection? You can certainly put the DataSource lookup name in a context init parameter, and that’s probably the most common use of context parameters today.

But then who does the work of turning the String parameter into an actual DataSource reference that all parts of the web app can share?

You can’t really put that code in a servlet, because which servlet would you choose to be The One To Lookup The DataSource And Store It In An Attribute? Do you really want to try to guarantee that one servlet in particular will always run first? Think about it.

Flex Your Mind

How could you solve this problem?

How could you initialize a web app with an object? Assume that you need the String context init parameter in order to create that object (think about the database example).

image with no caption

What she really wants is a listener.

She wants to listen for a context initialization event, so that she can get the context init parameters and run some code before the rest of the app can service a client.

She needs something that can be sitting there, waiting to be notified that the app is starting up.

But which part of the app could do the work? You don’t want to pick a servlet—that’s not a servlet’s job.

There’s no problem in a plain old standalone Java app, because you’ve got main()! But with a servlet, what do you do?

You need something else. Not a servlet or JSP, but some other kind of Java object whose sole purpose in life is to initialize the app (and possibly to uninitialize it too, cleaning up resources when it learns of the app’s demise...).

She wants a ServletContextListener

We can make a separate class, not a servlet or JSP, that can listen for the two key events in a ServletContext’s life—initialization (creation) and destruction. That separate class implements javax.servlet.ServletContextListener.

<<interface>>

ServletContextListener

contextInitialized(ServletContextEvent)

contextDestroyed(ServletContextEvent)

javax.servlet.ServletContextListener

We need a separate object that can:

  • Get notified when the context is initialized (app is being deployed).

    • Get the context init parameters from the ServletContext.

    • Use the init parameter lookup name to make a database connection.

    • Store the database connection as an attribute, so that all parts of the web app can access it.

  • Get notified when the context is destroyed (the app is undeployed or goes down).

    • Close the database connection.

A ServletContextListener class:

image with no caption
image with no caption

Flex Your Mind

What do you think the mechanism might be for making a listener be part of a specific web app?

Hint: how do you tell the Container about the other parts of your web app? Where might the Container discover your listener?

Tutorial: a simple ServletContextListener

Now we’ll walk through the steps of making and running a ServletContextListener. This is just a simple test class so that you can see how all the pieces work together; we’re not using the database connection example because you’d have to set up a database to make it work. But the steps are the same regardless of the code you put in your listener callback methods.

In this example, we’ll turn a String init parameter into an actual object—a Dog. The listener’s job is to get the context init parameter for the dog’s breed (Beagle, Poodle, etc.), then use that String to construct a Dog object. The listener then sticks the Dog object into a ServletContext attribute, so that the servlet can retrieve it.

The point is that the servlet now has access to a shared application object (in this case a Dog), and doesn’t have to read the context parameters. Whether the shared object is a Dog or a database connection doesn’t matter. The key is to use the init parameters to create a single object that all parts of the app will share.

image with no caption

In this example, we’ll put a Dog into a ServletContext.

Our Dog example:

  • The listener object asks the ServletContextEvent object for a reference to the app’s ServletContext.

  • The listener uses the reference to the ServletContext to get the context init parameter for “breed”, which is a String representing a dog breed.

  • The listener uses that dog breed String to construct a Dog object.

  • The listener uses the reference to the ServletContext to set the Dog attribute in the ServletContext.

  • The tester servlet in this web app gets the Dog object from the ServletContext, and calls the Dog’s getBreed() method.

Making and using a context listener

Maybe you’re still wondering how the Container discovers and uses the listener... You configure a listener the same way you tell the Container about the rest of your web app—through the web.xml Deployment Descriptor!

  1. Create a listener class

    image with no caption

    To listen for ServletContext events, write a listener class that implements ServletContextListener, put it in your WEB-INF/classes directory, and tell the Container by putting a <listener> element in the Deployment Descriptor.

  2. Put the class in WEB-INF/classes

    image with no caption

    Note

    (This isn’t the ONLY place it can go.... WEB-INF/classes is one of several places the Container can look for classes. We’ll cover the others in the Deployment chapter.)

  3. Put a <listener> element in the web.xml Deployment Descriptor

    <listener>
      <listener-class>
         com.example.MyServletContextListener
      </listener-class>
    </listener>

    Note

    Question for you: which part of the DD does the <listener> element go into? Does it go into a <servlet> element, or just under <web-app>? Think about it.

We need three classes and one DD

For our context listener test example, we need to write the classes and the web.xml file.

For ease of testing, we’ll put all of the classes in the same package: com.example

  1. The ServletContextListener

    MyServletContextListener.java

    This class implements ServletContextListener, gets the context init parameters, creates the Dog, and sets the Dog as context attribute.

    image with no caption
  2. The attribute class

    Dog.java

    The Dog class is just a plain old Java class. Its job is to be the attribute value that the ServletContextListener instantiates and sets in the ServletContext, for the servlet to retrieve.

    image with no caption
  3. The Servlet

    ListenerTester.java

    This class extends HttpServlet. Its job is to verify that the listener worked by getting the Dog attribute from the context, invoking getBreed() on the Dog, and printing the result to the response (so we’ll see it in the browser).

    image with no caption

Writing the listener class

It works just like other types of listeners you might be familiar with, such as Swing GUI event handlers. Remember, all we need to do is get the context init parameters to find out the dog breed, make the Dog object, and put the Dog into the context as an attribute.

image with no caption
image with no caption

Writing the attribute class (Dog)

Oh yeah, we need a Dog class—the class representing the object we’re going to store in the ServletContext, after reading the context init parameters.

image with no caption
image with no caption

Q:

Q: I thought I read somewhere that servlet attributes had to be Serializable...

A:

A: Interesting question. There are several different attribute types, and whether the attribute should be Serializable only matters with Session attributes. And the scenario in which it matters is only if the application is distributed across more than one JVM. We’ll talk all about that in the Sessions chapter.

There’s no technical need to have any attributes (including Session attributes) be Serializable, although you might consider making all of your attributes Serializable by default, unless you have a really good reason NOT to.

Think about it—are you really certain that nobody will ever want to use objects of that type as arguments or return values as part of a remote method call? Can you really guarantee that anyone who uses this class (Dog, in this case) will never run in a distributed environment?

So, although you aren’t required to make any attributes Serializable, you probably should if you can.

Writing the servlet class

image with no caption

This is the class that tests the ServletContextListener. If everything is working right, by the time the Servlet’s doGet() method runs for the first time, the Dog will be waiting as an attribute in the ServletContext.

image with no caption

Note

getAttribute() returns type Object! You need to cast the return!

But getInitParameter() returns a String. So you must cast the return of getAttribute(), but the return of getInitParameter() can be assigned directly to a String. So... don’t be fooled by bad exam code that doesn’t use a cast:

Dog d = ctx.getAttribute(“dog”); (Assume ctx is a ServletContext.)

Bad!!

Writing the Deployment Descriptor

Now we tell the Container that we have a listener for this app, using the <listener> element. This element is simple—it needs only the class name. That’s it.

image with no caption

This is the web.xml file inside the WEB-INF directory for this web app.

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">

  <servlet>
    <servlet-name>ListenerTester</servlet-name>
    <servlet-class>com.example.ListenerTester</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ListenerTester</servlet-name>
    <url-pattern>/ListenTest.do</url-pattern>
  </servlet-mapping>

  <context-param>
    <param-name>breed</param-name>
    <param-value>Great Dane</param-value>
  </context-param>

  <listener>
    <listener-class>
       com.example.MyServletContextListener
    </listener-class>
  </listener>

</web-app>

Note

We need a context init parameter for the app. The listener needs this to construct the Dog.

Note

Register this class as a listener. IMPORTANT: the <listener> element does NOT go inside a <servlet> element. That wouldn’t work because a context listener is for a ServletContext (which means application-wide) event. The whole point is to initialize the app BEFORE any servlets are initialized.

Compile and deploy

Let’s get it all working. The steps are:

image with no caption
  1. Compile the three classes

    They’re all in the same package...

  2. Create a new web app in Tomcat

    • Create a directory named listenerTest and place it inside the Tomcat webapps directory.

    • Create a directory named WEB-INF and place it inside the listenerTest directory.

    • Put your web.xml file in the WEB-INF directory.

    • Make a classes directory inside WEB-INF.

    • Make a directory structure inside classes that matches your package structure: a directory called com that contains example.

  3. Copy your three compiled files into your web app directory structure in Tomcat

    listenerTest/WEB-INF/classes/com/example/Dog.class
    listenerTest/WEB-INF/classes/com/example/ListenerTester.class
    listenerTest/WEB-INF/classes/com/example/MyServletContextListener.class
  4. Put your web.xml Deployment Descriptor into the WEB-INF directory for this web app

    listenerTest/WEB-INF/web.xml
  5. Deploy the app by shutting down and restarting Tomcat

Try it out

Bring up your browser and let’s hit the servlet directly. We didn’t bother making an HTML page, so we’ll access the servlet by typing in the URL from the servlet mapping in the DD (ListenTest.do).

image with no caption

Troubleshooting

If you get a NullPointerException, you didn’t get a Dog back from getAttribute(). Check the String name used in setAttribute() and make sure it matches the String name you’re using in getAttribute().

Recheck your web.xml and make sure the <listener> is registered.

Try looking at the server logs and see if you can find out if the listener is actually being called.

To make it as confusing as possible, we gave everything a subtly different name. We want to make sure you’re paying attention to how these names are used, and when you name everything the same, it’s tough to tell how the names affect your app.

Servlet class name: ListenerTester.class

Web app directory name: listenerTest

URL pattern mapped to this servlet: ListenTest.do

Note

Be careful about whether it’s Listener or Listen, Tester or Test.

The full story...

Here’s the scenario from start (app initialization) to finish (servlet runs). You’ll see in step 11 we condensed the Servlet initialization into one big step.

  1. Container reads the Deployment Descriptor for this app, including the <listener> and <context-param> elements.

    image with no caption
  2. Container creates a new ServletContext for this application, that all parts of the app will share.

    image with no caption
  3. Container creates a name/value pair of Strings for each context init parameter. Assume we have only one.

    image with no caption
  4. Container gives the ServletContext references to the name/value parameters.

    image with no caption
  5. Container creates a new instance of the MyServletContextListener class.

    image with no caption
  6. Container calls the listener’s contextInitialized() method, passing in a new ServletContextEvent. The event object has a reference to the ServletContext, so the event-handling code can get the context from the event, and get the context init parameter from the context.

    image with no caption
  7. Listener asks ServletContextEvent for a reference to the ServletContext.

    image with no caption
  8. Listener asks ServletContext for the context init parameter “breed” .

    image with no caption
  9. Listener uses the init parameter to construct a new Dog object.

    image with no caption
  10. Listener sets the Dog as an attribute in the ServletContext.

    image with no caption
  11. Container makes a new Servlet (i.e., makes a new ServletConfig with init parameters, gives the ServletConfig a reference to the ServletContext, then calls the Servlet’s init() method).

    image with no caption
  12. Servlet gets a request, and asks the ServletContext for the attribute “dog”.

    image with no caption
  13. Servlet calls getBreed() on the Dog (and prints that to the HttpResponse).

    image with no caption

Listeners: not just for context events...

image with no caption

Where there’s a lifecycle moment, there’s usually a listener to hear about it. Besides context events, you can listen for events related to context attributes, servlet requests and attributes, and HTTP sessions and session attributes.

Relax

You don’t have to know all of the listener API.

Other than ServletContextListener, you really don’t need to memorize the methods of each of the listener interfaces. But... you DO need to know the kinds of events that you can listen for.

The exam objectives are clear: you’ll be given a scenario (a developer’s goal for an application) and you’ll need to decide which is the right type of listener, or whether it’s even POSSIBLE to be notified of that lifecycle event.

Note

Note: we don’t talk about sessions until the next chapter, so don’t worry about it if you don’t yet know what an HTTP session is or why you care...

The eight listeners

Scenario

Listener interface

Event type

You want to know if an attribute in a web app context has been added, removed, or replaced.

javax.servlet.ServletContextAttributeListener

attributeAdded

attributeRemoved

attributeReplaced

ServletContextAttributeEvent

You want to know how many concurrent users there are. In other words, you want to track the active sessions. (We cover sessions in detail in the next chapter).

javax.servlet.http.HttpSessionListener

sessionCreated

sessionDestroyed

HttpSessionEvent

You want to know each time a request comes in, so that you can log it.

javax.servlet.ServletRequestListener

requestInitialized

requestDestroyed

ServletRequestEvent

You want to know when a request attribute has been added, removed, or replaced.

javax.servlet.ServletRequestAttributeListener

attributeAdded

attributeRemoved

attributeReplaced

ServletRequestAttributeEvent

You have an attribute class (a class for an object that will be stored as an attribute) and you want objects of this type to be notified when they are bound to or removed from a session.

javax.servlet.http.HttpSessionBindingListener

valueBound

valueUnbound

HttpSessionBindingEvent

You want to know when a session attribute has been added, removed, or replaced.

javax.servlet.http.HttpSessionAttributeListener

attributeAdded

attributeRemoved

attributeReplaced

HttpSessionBindingEvent

Note

Watch out for this naming inconsistency! The Event for HttpSessionAttributeListener is NOT what you expect (you expect HttpSessionAttributeEvent).

You want to know if a context has been created or destroyed.

javax.servlet.ServletContextListener

contextInitialized

contextDestroyed

ServletContextEvent

You have an attribute class, and you want objects of this type to be notified when the session to which they’re bound is migrating to and from another JVM.

javax.servlet.http.HttpSessionActivationListener

sessionDidActivate

sessionWillPassivate

HttpSessionEvent

Note

It’s NOT “HttpSessionActivationEvent”

The HttpSessionBindingListener

You might be confused about the difference between an HttpSessionBindingListener and an HttpSessionAttributeListener. (Well, not you, but someone you work with.)

A plain old HttpSessionAttributeListener is just a class that wants to know when any type of attribute has been added, removed, or replaced in a Session. But the HttpSessionBindingListener exists so that the attribute itself can find out when it has been added to or removed from a Session.

image with no caption
package com.example;

import javax.servlet.http.*;

public class Dog implements HttpSessionBindingListener {
   private String breed;

   public Dog(String breed) {
     this.breed=breed;
   }

   public String getBreed() {
     return breed;
   }
   public void valueBound(HttpSessionBindingEvent event) {
     // code to run now that I know I'm in a session
   }

   public void valueUnbound(HttpSessionBindingEvent event) {
     // code to run now that I know I am no longer part of a session
   }
}

Note

This time the Dog attribute is ALSO a Listener... listening for when the Dog itself is added or removed from a Session. (Note: binding listeners are NOT registered in the DD... it just happens automatically.)

Note

They use the word “bound” and “unbound” to mean “added to” and “removed from”.

Q:

Q: OK. I get how it works. I get that the Dog (an attribute that’ll be added to a session) wants to know when it’s in or out of a session. What I don’t get is WHY.

A:

A: If you know anything about Entity beans... then you can picture this capability as a kind of “poor man’s entity bean”. If you don’t know about entity beans, you should run to your nearest bookstore and buy two copies of Head First EJB (one for you, one for your significant other so you can share special moments discussing it).

In the meantime, here’s a way to think about it—imagine the Dog is a Customer class, with each active instance representing a single customer’s info for name, address, order info, etc. The real data is stored in an underlying database. You use the database info to populate the fields of the Customer object, but the issue is how and when do you keep the database record and the Customer info synchronized? You know that whenever a Customer object is added to a session, it’s time to refresh the fields of the Customer with this customer’s data from his record in the database. So the valueBound() method is like a kick that says, “Go load me up with fresh data from the database... just in case it changed since the last time I was used.” Then valueUnbound() is a kick that says, “Update the database with the value of the Customer object fields.”

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset