A JSP page is made out of a page template, which consists of HTML code and JSP elements such as scripting elements, directive elements, and action elements. In the previous chapter, after explaining how to install Java, Tomcat, and Eclipse, I introduced you to JSP and explained JSP’s role within web applications. In this chapter, I’ll describe in detail the first two types of JSP elements. For the action elements, refer to the next chapters.
Introduction
Scripting elements consist of code delimited by particular sequences of characters. The scriptlets, which you encountered in the examples in Chapter 1 and delimited by the pair <% and %>, are one of the three possible types of scripting elements. The other two are declarations and expressions.
All scripting elements are Java fragments capable of manipulating Java objects, invoking their methods, and catching Java exceptions. They can send data to the output, and they execute when the page is requested. Tomcat defines several implicit objects: application, config, exception, out, pageContext, request, response, and session.
Directive elements are messages to the JSP container (i.e., Tomcat). Their purpose is to provide information on the page itself necessary for its translation. As they have no association with each individual request, directive elements do not output any text to the HTML response.
Besides page, the other directives available in JSP pages are include and taglib.
In addition to the standard action elements, JSP also provides a mechanism that lets you define custom actions, in which a prefix of your choice replaces the prefix jsp of the standard actions. The tag extension mechanism lets you create libraries of custom actions, which you can then use in all your applications. Several custom actions became so popular within the programming community that Sun Microsystems (now Oracle) decided to standardize them. The result is JSTL, the JSP Standard Tag Library.
The Expression Language (EL) is an additional JSP component that provides easy access to external objects (i.e., Java beans). EL was introduced in JSP 2.0 as an alternative to the scripting elements, but you can also use EL and scripting elements together.
In the next sections, I will first go through the scripting elements, because they are easier to understand and you can use them to glue together the rest. Then, I will describe the implicit objects and the directives. To help you find the correct examples in the software package for this chapter, I divided them in folders named according to the section title and the functionality tested (e.g., request object - body).
Scripting Elements and Java
Scripting elements let you embed Java code in an HTML page. Every Java executable—whether it’s a free-standing program running directly within a runtime environment or a servlet executing in a container such as Tomcat—boils down to instantiating classes into objects and executing their methods. This might not be so apparent with JSP, since Tomcat wraps every JSP page into a class of type Servlet behind the scenes, but it still applies.
A Java method consists of a sequence of operations—executed when the method is called—with the scope to instantiate objects, allocate memory for variables, calculate expressions, perform assignments, or execute other specific instructions.
In this section, I’ll summarize the syntax of Java while keeping JSP in mind.
Scriptlets
Expressions
You can use within an expression scripting element any Java expression, provided it results in a value. In practice, it means that every Java expression will do, except the execution of a method of type void. For example, <%=(condition) ? "yes" : "no"%> is valid, because it calculates to a string. You would obtain the same output with the scriptlet <%if (condition) out.print("yes") else out.print("no");%>.
Note that an expression is not a Java statement. Accordingly, it has no semicolon at the end.
Declarations
A declaration scripting element is a Java variable declaration enclosed between <%! and %>. It results in an instance variable shared by all requests for the same page.
Data Types and Variables
Java Data types
Name | Class | Description |
---|---|---|
byte | Byte | 1-byte signed integer (–128 to +127) |
short | Short | 2-byte signed integer (–32,768 to +32,767) |
int | Integer | 4-byte signed integer (–2,147,483,648 to +2,147,483,647) |
Long | Long | 8-byte signed integer (approximately –1019 to +1019) |
Float | Float | 32-bit signed floating point (8-bit exponent, 23-bit precision) |
Double | Double | 64-bit signed floating point (11-bit exponent, 52-bit precision) |
Char | Character | 16-bit unsigned Unicode |
boolean | Boolean | Either true or false |
The second column of Table 2-1 gives you the names of the so-called wrapper classes that Java makes available for each primitive type. These classes provide some useful static methods to manipulate numbers. For example, Integer.parseInt(String s, int radix) interprets a string as a number in the base set by the second argument and returns it as an int value (e.g., Integer.parseInt("12", 16) and Integer.parseInt("10010", 2) both return 18).
Programs in Java can be platform-independent because all platform dependencies are “hidden” inside libraries. The wrapper classes I just mentioned are in the java.lang library, together with dozens of other general classes such as String and Math. You can find the full documentation of the Java 13 platform at https://docs.oracle.com/en/java/javase/13/docs/api/index.html.
The final keyword in the last example of declarations makes the variable unchangeable. This is how you define constants in Java. The static keyword indicates that a variable is to be shared by all objects within the same application that are instantiated from the class.
The first declaration means that a new variable is created for each incoming HTTP client request; the second one means that a new variable is created for each new instance of the servlet; and the third one means that the variable is shared among all instances of the servlet.
Tomcat converts each JSP page into a subclass of the HTTP Servlet class (javax.servlet.http.HttpServlet). Normally, Tomcat instantiates each one of these classes only once and then creates a Java thread for each incoming request. It then executes the same servlet object within each thread. If the application runs on a distributed environment or for high numbers of requests, Tomcat can instantiate the same servlet more than once. Therefore, only the third declaration guarantees that the variable will be shared among all requests.
Tomcat keeps the servlet code buried deep in the folder named work. For example, the servlet generated from webappsROOT esta.jsp is in workCatalinalocalhost\_orgapachejsp est and is named a_jsp.java.
You’re free to name your variables as you like, though your case-sensitive string of characters must begin with a letter, a dollar, or an underscore and not contain a space. That said, be aware that the following keywords are reserved and will cause a compilation error: abstract, assert, boolean, break, byte, case, catch, char, class, const, continue, default, do, double, else, enum, extends, final, finally, float, for, goto, if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, throw, throws, transient, try, void, volatile, and while. Whenever possible, use capital letters for constants. It is not necessary, but it makes the code more readable and is a well-established coding practice.
Escaped Special Characters
Character | Escaped |
---|---|
backslash | \ |
backspace | |
carriage return |
|
double quote | " |
form feed | f |
line feed |
|
single quote | ' |
tab |
|
Objects and Arrays
To create an object of a certain type (i.e., to instantiate a class) and allocate memory, use the keyword new. In the following example, a new Java object of type String is created:
String newString= new String();
This creates an object of type Integer with value 55.
you have a table with two rows and three columns, with all elements initialized to zero.
Operators, Assignments, and Comparisons
There are no surprises with the binary operators—that is, the operators that require two operands. They include the expected addition, subtraction, multiplication, division, and modulus (i.e., the remainder of an integer division) operators. When applied to string, the addition operator concatenates them.
You can assign the value of an expression of one type to a variable of another type, but with some restrictions. With numeric types, you can only assign values to variables that are of the same type or “larger.” For example, you can assign an int value to a variable of type long, but to assign a long value to an int variable, you’d have to typecast (i.e., downcast) the value, as in int iVar = (int)1234567L;. Be careful with that, because you might lose precision when downcasting floating-point numbers!
You can assign objects to variables of other types, but only if the type of the variable is a superclass of the class from which you instantiated the object. Similarly to the downcasting of numeric types, you can typecast a value of a superclass into a variable of a subclass type.
As perhaps you expected, b1 and b2 turn out to be true, but b3 is false, although s3 was set to "abc"! The problem is that comparison operators don’t look inside the objects. They only check whether the objects are the same instance of a class, not whether they hold the same value. Therefore, as long as you shift around the "abc" string, the compiler keeps referring to the same instance of a literal string, and everything behaves as expected. However, when you create a different instance of "abc", the check for equality fails. The lesson to be learned is that if you want to compare the content of objects, you have to use the equals method. In this example, s1.equals(s3) would have returned true.
For objects, you also have the comparison operator instanceof, which isn’t available for primitive data types like int. For example, ("abc" instanceof String) calculates to true. Be aware that an object isn’t only an instance of the class it was instantiated from, but it’s also an instance of all its superclasses up to and including Object, which is the superclass of all classes. It makes sense: a String is also an Object, even if the reverse often is not true.
With && for logical and, || for logical or, and ! for logical not, you can concatenate comparisons to form more complex conditions. For example, ((a1 == a2) && !(b1 || b2)) calculates to true only if a1 equals a2 and both boolean variables b1 and b2 are false.
Selections
You can omit the else part.
At the very least, you’ll calculate the expression only once. Note that if you omit a break, execution continues to the following case.
Iterations
The initial-assignment is executed only once, before entering the loop. The statements are then repeatedly executed as long as the end-condition is satisfied. As the end-condition is checked before executing the statements, they are not executed at all if the end-condition is false from the beginning. The iteration-expression is executed at the end of each iteration, before the end-condition is checked to see whether the loop should be reentered for a new iteration.
The do-while statement checks the end-condition at the end of an iteration instead of at the beginning, like the for and while loops do. As a result, the statements inside a do-while loop are always executed at least once, even when the end-condition is false from the beginning.
Implicit Objects
The most commonly used implicit objects defined by Tomcat are out and request, followed by application and session. But I will go through them in alphabetical order, for ease of reference.
Whether you create objects within JSP pages or Tomcat implicitly creates them for you, you cannot use them properly unless you know in which scope they are available. There are four possible scopes. In order of increasing generality, they are page, request, session, and application. You will learn more about them in the following sections.
The application Object
The application object is an instance of the class org.apache.catalina.core.ApplicationContextFacade, which Tomcat defines to implement the interface javax.servlet.ServletContext. It provides access to the resources shared within the web application. For example, by adding an attribute (which can be an object of any type) to application, you can ensure that all JSP files that make up your web application have access to it.
Example: Using an Attribute to Enable and Disable Conditional Code
do_it.jsp
do_it_not.jsp
Create new jsps in the same test Project folder created in Chapter 1 and copy the code or place them in the WebContent folder. At last, start Tomcat in Eclipse and type in your browser the URL when you want to enable the conditional code:
http://localhost:8080/test/do_it.jsp
Until you disable it by typing the URL
http://localhost:8080/test/do_it_not.jsp
or by restarting Tomcat, the conditional code will remain enabled in all pages of your application. Notice that in the example do_it.jsp only sets the attribute do_it to an empty string, but you can also define different values to have a finer selection of code to be activated.
Note that you can use the same mechanism to switch on and off HTML code.
The config Object
The config object is an instance of the org.apache.catalina.core.StandardWrapperFacade class, which Tomcat defines to implement the interface javax.servlet.ServletConfig. Tomcat uses this object to pass information to the servlets.
The exception Object
The exception object is an instance of a subclass of Throwable (e.g., java.lang.NullPointerException) and is only available in error pages.
stack_trace.jsp
Notice the directive <%@page isErrorPage="true"%>, without which the implicit object exception is not defined.
cause_exception.jsp
Notice the <%@page errorPage="stack_trace.jsp"%> directive, which links the error page of Listing 2-4 to the occurrence of exceptions. To cause a NullPointerException, the page requests a parameter that doesn’t exist and then accesses it. If you use try/catch to trap the exception, obviously the error page is not executed.
To see the two pages in action, create new jsps in the same test Project folder created in Chapter 1 and copy the code or place them in the WebContent folder. At last, start Tomcat in Eclipse and type in a browser the URL
http://localhost:8080/test/cause_exception.jsp.
The out Object
You use the out object in JSP as you use the System.out object in Java: to write to the standard output. The standard output for a JSP page is the body of the HTML response sent back to the client. Therefore, the scriptlet <%out.print(expression);%> causes the result of the expression to be displayed in the client’s browser. You can achieve the same result by simply typing <%=expression%>.
Clearly, it makes no sense to use the first two formats when you need to write literal values. To decide whether to use a scriptlet delimited by <%..%> or an expression delimited by <%=..%>, you should look at the surrounding code and decide what makes it as easy to read as possible.
Be aware that if you try to print an object or an array by sticking its name into a print statement, you won’t necessarily see its content in the output. If the object doesn’t support a toString() method , you’ll see a mysterious string representing the reference to the object.
Most manuals state that out is an instance of the javax.servlet.jsp.JspWriter class, which you can use to write into the response. This is not entirely correct, because JspWriter is an abstract class, and as such, it cannot be instantiated. In reality, out is an instance of the nonabstract class org.apache.jasper.runtime.JspWriterImpl, which extends JspWriter. Tomcat defines JspWriterImpl precisely to implement the JspWriter methods. For all practical purposes, this is inconsequential to you, but some of you sharp-eyed readers might have thought that I was talking about instantiating an abstract class. It usually pays to be precise.
The JspWriter class includes the definition of a handful of fields. You won’t need them, but mentioning them gives me the opportunity to give you some useful information.
The autoFlush field tells you whether the JspWriter is flushed automatically when its buffer fills up or whether an IOException is thrown upon overflow. The default for out is true, which means that Tomcat will send a partial response to the client if the buffer fills up. You can set it to false with the directive <%@page autoFlush="false"%>, and you should do so if you expect the client to be an application. Sending the response in “chunks” is perfectly OK when the client is a browser, but an application will probably expect the response in a single block. If you expect the client to be an application and set autoFlush to false, you should also use <%@page buffer="size-in-kb"%>, to ensure that the output buffer is large enough to store your largest response. The field autoFlush is protected, but you can obtain its value with the isAutoFlush method.
The bufferSize field is the size in bytes of the output buffer. The default for out is 8192 bytes. It’s a protected field, but you can obtain its value with the getBufferSize method.
There are also three constant integer fields (DEFAULT_BUFFER, NO_BUFFER, and UNBOUNDED_BUFFER of type public static final int), but you can safely ignore them. Just for the record, they’re respectively used to test whether the JspWriter is buffered (and uses the default buffer size), isn’t buffered, or is buffered with an unbounded buffer. Besides the fact that you have no variable or attribute to check against these values, you’re in any case well served by the getBufferSize method (which returns 0 if the output is not buffered).
Here, fun(arg) is executed, and both arg and the value returned by fun(arg) are automatically converted to strings to be concatenated with the rest.
However, it would be less efficient, because first you would be copying a portion of the original array—an operation you don’t need when using write.
You’re not likely to use any of the other methods, and you should definitely avoid using close, which closes the output stream. Tomcat closes the stream when it is safe to do so, and you don’t want to fiddle with it.
The pageContext Object
Most manuals state that pageContext is an instance of the javax.servlet.jsp.PageContext class to access all objects and attributes of a JSP page. Similar to what I said concerning JspWriter, this is only partly true, because this class, like JspWriter, is also abstract. In reality, pageContext is an instance of the nonabstract class org.apache.jasper.runtime.PageContextImpl, which extends PageContext.
The PageContext class defines several fields, including PAGE_SCOPE, REQUEST_SCOPE, SESSION_SCOPE, and APPLICATION_SCOPE, which identify the four possible scopes. It also supports more than 40 methods, about half of which are inherited from the javax.servlet.jsp.JspContext class.
You have to pay particular attention when using the removeAttribute method , which accepts either one or two arguments.
For example, pageContext.removeAttribute("attrName") removes the attribute from all scopes, while the following code only removes it from the page scope:
pageContext.removeAttribute("attrName", PAGE_SCOPE)
The request Object
The request variable gives you access within your JSP page to the HTTP request sent to it by the client. It’s an instance of the org.apache.catalina.connector.RequestFacade class, which Tomcat defines to implement the javax.servlet.http.HttpServletRequest and javax.servlet.ServletRequest interfaces.
More on Request Parameters and Client Info
Note that in the request generated by a URL like this:
http://localhost:8080/my_page.jsp?aaa&bbb=&ccc=3
the parameters aaa and bbb exist but are set to the empty string. Therefore, getParameter does not return null for them.
As you already saw in Chapter 1, the request can include more than one value associated with the same parameter. For example, the following URL generates a request with three values for the parameter aaa:
http://localhost:8080/my_page.jsp?aaa&aaa=4&aaa=7
But if the viewer were in a country where a language other than English is spoken, you might get other locales (e.g., "de_DE" for German). If you had a multilingual site, the locale would tell you the working language of your user. You could check whether you support it and, if you do, set it as a default for the response.
The getRemoteHost method , which returns the client’s hostname (or that of its proxy server), could be useful in a similar way, because you could look at the string after the last dot to identify foreign domain names (e.g., it for Italy). Unfortunately, in many cases, the remote address cannot be resolved to a name, and you end up getting only the client’s IP address, exactly as if you had called the getRemoteAddress method . Services available on the Internet let you resolve an IP address to the country where the system resides, but you might have to pay for a reliable service.
You cannot mix methods that handle parameters with methods that handle the request content or methods that access the request content in different ways. For example, if you execute any two of the methods request.getParameter, getReader, and getInputStream when handling a request, the second one you execute will fail.
Example: Listing the Headers
req_headers.jsp
At last, start Tomcat in Eclipse and type in a browser the URL
http://localhost:8080/test/req_headers.jsp
Example: Reading the Request Body
req_getInputStream.jsp
req_getReader.jsp
Now, create new jsps in the same test Project folder created in Chapter 1 and copy the code or place them in the WebContent folder. At last, start Tomcat in Eclipse and type in a browser the URL
http://localhost:8080/test/req_getInputStream.jsp
The response Object
The response variable gives you access within your JSP page to the HTTP response to be sent back to the client. It is an instance of the org.apache.catalina.connector.ResponseFacade class, which Tomcat defines to implement the interfaces javax.servlet.http.HttpServletResponse and javax.servlet.ServletResponse.
The HttpServletResponse interface includes the definition of 41 status codes (of type public static final int) to be returned to the client as part of the response. The HTTP status codes are all between 100 and 599. The range 100–199 is reserved to provide information, 200–299 to report successful completion of the requested operation, 300–399 to report warnings, 400–499 to report client errors, and 500–599 to report server errors.
The normal status code is SC_OK (200), and the most common error is SC_NOT_FOUND (404), which occurs when the client requests a page that doesn’t exist. Working with Tomcat, the most common server error is SC_INTERNAL_SERVER_ERROR (500). You get it when there is an error in a JSP. You can use these constants as arguments of the sendError and setStatus methods.
The session Object
The term session refers to all the interactions a client has with a server from the moment the user views the first page of an application to the moment they quit the browser (or the session expires because too much time has elapsed since the last request).
When Tomcat receives an HTTP request from a client, it checks whether the request contains a cookie that by default is named JSESSIONID. If it doesn’t find it, it creates the cookie with a unique value and attaches it to the response. This establishes the beginning of a session. If the client’s browser accepts cookies, it attaches that cookie to all subsequent requests it sends to the same server.
By doing so, as long as the user keeps his or her browser running and the session doesn’t timeout, you don’t need to reload the user’s preferences from a database.
The variable session is an instance of the org.apache.catalina.session.StandardSessionFacade class, which Tomcat defines to implement the javax.servlet.http.HttpSession interface.
Alternatively, you can also change Tomcat’s default timeout by inserting the <session-config> element in the confweb.xml file you find inside the Tomcat home directory.
Directive Elements
JSP pages use directive elements to pass to Tomcat data about themselves. This data influences the translation process from a script file to a Java servlet class. As directives only play a role when a JSP page is recompiled after you modify it, they have no specific effect on the individual HTML responses.
The page Directive
The page directive defines several page-dependent properties expressed through attributes. These properties should appear only once in a JSP page (unless the multiple instances all have the same value, but why should you do that?). You can write more than one page directive in a JSP page, and they will all apply. Their order or position within the page is generally irrelevant.
In addition to language, contentType, and import, the page directive also supports autoFlush, buffer, errorPage, extends, info, isELIgnored, isErrorPage, isScriptingEnabled, isThreadSafe, pageEncoding, session, and trimDirectiveWhitespaces.
Listing 2-8 shows you a simple program that utilizes the isThreadSafe attribute to test concurrency. Create new jsps in the same test Project folder created in Chapter 1 and copy the code or place them in the WebContent folder. At last, start Tomcat in Eclipse and type in a browser the URL
concurrency.jsp
The program declares the instance variable k, copies it to the variable j, increments j, waits for five seconds, and copies the incremented j back to k. It also displays k at the beginning and at the end.
If you reload the page several times, you’ll see that k is increased every time the page refreshes. Now view the page in another browser (not just another browser window, because caching plays funny tricks); for example, view it in Chrome if you normally use Firefox. If you keep reloading the page in the two browsers, you’ll see the k keeps increasing regardless of which browser you’re looking at. This is because k is an instance variable.
Now reload the page in the first browser and then immediately in the second browser. Do you notice how the second browser takes longer to refresh? This is because you’ve set isThreadSafe="false", and Tomcat doesn’t execute the servlet code for the two requests at the same time. However, k keeps increasing across the browsers with each page refresh.
Now remove the page directive that sets isThreadSafe to false and repeat the test. When you reload the page on both browsers almost simultaneously, they refresh the page at the same time but with the same value of k! This is because the second execution of the servlet starts while the first one is “holding” for five seconds.
I introduced the five-second delay to be sure that you would see the problem. Without the delay, the time interval between incrementing j and saving it back to k would be vanishingly small. Therefore, you might keep trying for years and never see the problem. Nevertheless, to rely on “it will never happen” when developing code, especially when concurrency plays a role, is a very bad practice. Other factors might influence the timing, and suddenly you might start seeing a problem once a day or even more rarely. It could have a damaging effect on how users consider your website.
The price paid for playing it safe with isThreadSafe is that it can slow down execution significantly. Fortunately, there’s a better way to make the threads safe than relying on Tomcat. Look at Listing 2-9 that you can configure as Listing 2-8 and then type in a browser the URL
concurrency2.jsp
You protect the critical part of the code by enclosing it in a synchronized block. The syncK variable , being defined in a declaration element, is an instance variable shared like k among all the requests. I haven’t used k because synchronized requires an object. In this simple case, instead of creating a new object specifically to protect the code, I could have used this, representing the servlet itself. But in general, if there were more than one block of code to protect, it wouldn’t be a good idea. The best strategy to maximize efficiency, besides staying locked as little as possible, is to use specific locks.
I spent a bit of time on the attribute isThreadSafe because concurrency often is not well understood or implemented and causes intermittent bugs that are devilish to eliminate. Therefore, avoid implementing concurrency in the UI layer and delegate as much as possible its handling to the container running the web application. In fact, Tomcat automatically manages scheduling and synchronization of jobs and it is also possible to configure Tomcat concurrency, but it is over the scope of this book.
extends tells Tomcat which class the servlet should extend.
info defines a string that the servlet can access with its getServletInfo() method .
isELIgnored tells Tomcat whether to ignore EL expressions.
isScriptingEnabled tells Tomcat whether to ignore scripting elements.
pageEncoding specifies the character set used in the JSP page itself.
session tells Tomcat to include or exclude the page from HTTP sessions.
The bottom line is that, in most occasions, you can leave these additional attribute sets to their default values.
The include Directive
JSPF stands for JSP Fragment, although, more recently, chunks of JSP code have been called JSP Segments, rather than Fragments. In fact, any text file with any extension will do.
As Tomcat does the merging before any translation, the raw content of the included file is pasted into the page without any check. All the HTML tags and JSP variables defined before the line containing the directive are available to the included code. This directive can be very useful, but use it sparingly, because it can easily lead to unmaintainable code, with bits and pieces spread all over the place.
The taglib Directive
You will find the description of JSTL and how to use it in the next chapters. In section “JSP’s Tag Extension Mechanism” of the same chapter, I’ll explain the possible advantages of creating your own libraries of tags and how to do it. For the time being, simply remember that the taglib directive tells Tomcat what libraries to load and where they are.
Summary
In this chapter, you learned all scripting and directive JSP elements.
I started by explaining the Java syntax used in scriptlets and the implicit objects defined by Tomcat, with several examples showing how to use them.
After that, I described the JSP directives.
In the next chapter, you will learn how to build complex JSP applications.