[ Team LiB ] Previous Section Next Section

16.5 Using Declarations

I have described two of the three JSP scripting elements in this chapter so far: scriptlets and expressions. There's one more called a declaration element, which is used to declare Java variables and methods in a JSP page. My advice is this: don't use it. Let me explain why.

In general, Java variables can be declared either within a method or outside the body of all methods in a class, like this:

public class SomeClass {
  // Instance variable
  private String anInstanceVariable;
  
  // Method
  public void doSomething(  ) {
    String aLocalVariable;
  }
}

A variable declared outside the body of all methods is called an instance variable. Its value can be accessed from any method in the class, and it keeps its value even when the method that sets it returns. A variable declared within the body of a method is called a local variable; it can be accessed only within the method where it's declared. When the method returns, the local variable disappears.

Recall from Chapter 3 that a JSP page is turned into a servlet class when it's first requested, and the JSP container creates one instance of this class. If more than one user requests the same page at the same time, the single instance is used for all requests. Each user is assigned what is called a thread in the server, and each thread executes the same method in the JSP implementation class instance. When more than one thread executes the same code, you have to make sure the code is thread safe. This means that the code must behave the same when many threads are executing as when just one thread executes the code.

Multithreading and thread-safe code strategies are best left to experienced programmers. However, using a JSP declaration element to declare variables exposes your page to multithreading problems. That's because a variable that's declared using a JSP declaration element ends up as an instance variable in the generated servlet, not as a local variable in a method. All threads share the instance variable, so if one thread changes its value, the new value is seen by all threads. To put this in JSP terms, if the instance variable is changed because one user accesses the page, all users accessing the same page will use the new value.

When you declare a variable within a scriptlet element instead of in a JSP declaration block, the variable ends up as a local variable in the generated servlet's request processing method. Each thread has its own copy of a local variable, so a local variable doesn't cause any problems even when more than one thread executes the same code. If the value of a local variable is changed, it will not affect the other threads.

That being said, let's look at a simple example. We use two int variables; one declared as an instance variable using a JSP declaration, and the other declared as a local variable with a scriptlet. We increment them both by one and display the new values. Example 16-3 shows the test page.

Example 16-3. Using a declaration element (counter.jsp)
<%@ page language="java" contentType="text/html" %>
<%! 
  int globalCounter = 0; 
%>
<html>
  <head>
    <title>A page with a counter</title>
  </head>
  <body bgcolor="white">
    This page has been visited: <%= ++globalCounter %> times.
    <p>
    <% 
      int localCounter = 0;
    %>
    This counter never increases its value: <%= ++localCounter %>
  </body>
</html>

The JSP declaration element is right at the beginning of the page in Example 16-3, starting with <%! and ending with %>. Note the exclamation point (!) in the start identifier; that's what makes it a declaration as opposed to a scriptlet. The declaration element declares an instance variable named globalCounter, shared by all requests for the page. In the page body, a JSP expression increments the variable's value and adds it to the page. Next comes a scriptlet, enclosed by <% and %>, that declares a local variable named localCounter. It is then incremented and added to the page by the last expression element.

When you run this example, the globalCounter value increases every time you load the page, but localCounter stays the same. Again, this is because globalCounter is an instance variable, while localCounter is a local variable.

In this example, nothing terribly bad happens if more than one user hit the page at the same time. The worst that could happen is that you skip a number or show the same globalCounter value twice. This can happen if two requests come in at the same time, and both requests increment the value before it's inserted in the response. You can imagine the consequences, however, if you use an instance variable to save something more important, such as a customer's credit-card number or other sensitive information. So even though it may be tempting to create an instance variable (using a JSP declaration) to keep a value such as a counter between requests, I recommend that you stay away from this technique. Using objects in the session and application scopes, as described in Chapter 10, is a far better approach.

A JSP declaration element can also be used to declare a method that can then be used in scriptlets in the same page. The only harm this can cause is that your JSP pages end up containing too much code, making it hard to maintain the application. I recommend that you use beans, custom actions, or EL functions instead, but to be complete, Example 16-4 shows an example of how it can be done.

Example 16-4. Method declaration and use (color.jsp)
<%@ page language="java" contentType="text/html" %>
<%!
  String randomColor(  ) {
    java.util.Random random = new java.util.Random(  );
    int red = (int) (random.nextFloat(  ) * 255);
    int green = (int) (random.nextFloat(  ) * 255);
    int blue = (int) (random.nextFloat(  ) * 255);
    return "#" + 
      Integer.toString(red, 16) + 
      Integer.toString(green, 16) + 
      Integer.toString(blue, 16);
    }
%>
<html>
  <head>
    <title>Random Color</title>
  </head>
  <body bgcolor="white">
  
    <h1>Random Color</h1>
  
    <table bgcolor="<%= randomColor(  ) %>" >
      <tr><td width="100" height="100">&nbsp;</td></tr>
    </table>
  
  </body>
</html>

The method named randomColor( ), declared between <%! and %>, returns a randomly generated String in a format that can be used as an HTML color value. This method is then called from an expression element to set the background color for a table. Every time you reload this page, you see a single table cell with a randomly selected color.

16.5.1 jspInit() and jspDestroy( )

If you know a bit about servlets, you know that a servlet has two methods the container calls when the servlet is loaded and shut down, respectively. These methods are called init( ) and destroy( ), and they allow the servlet to initialize instance variables when it's loaded and clean up when it's shut down. As you already know, a JSP page is turned into a servlet, so it has the same capability. However, with JSP, the methods are called jspInit( ) and jspDestroy( ) instead.

Again, I recommend that you don't declare any instance variables for your JSP pages. If you follow this advice, there's also no reason to declare the jspInit( ) and jspDestroy( ) methods. But I know you're curious, so here's an example of how they can be used.

Expanding on Example 16-3, the jspInit( ) method can set an instance variable to a java.util.Date( ) object, which represents the date and time when the page was initialized. This variable can then be used in the page to show when the counter was started:

<%@ page language="java" contentType="text/html" %>
<%@ page import="java.util.Date" %>
<%! 
  int globalCounter = 0; 
  java.util.Date startDate;
  
  public void jspInit(  ) {
    startDate = new java.util.Date(  );
  }
  
  public void jspDestroy(  ) {
    ServletContext context = getServletConfig().getServletContext(  );
    context.log("test.jsp was visited " + globalCounter +
      " times between " + startDate + " and " + (new Date(  )));
  }
%>
<html>
  <head>
    <title>A page with a counter</title>
  </head>
  <body bgcolor="white">
    This page has been visited: <%= ++globalCounter %> times
    since <%= startDate %>.
  </body>
</html>

The jspDestroy( ) method retrieves a reference to the ServletContext for the page and writes a message to the container's log file. As you may recall, the implicit application variable contains a reference to the ServletContext, so you may wonder why it's not used here. The reason is that the implicit variables are local variables in the method that the JSP container generates to process the page requests; hence, they aren't available to the methods you declare yourself.

    [ Team LiB ] Previous Section Next Section