Skip Navigation LinksHome > Documentation > Thinking In > Thinking in Enterprise Java

Thinking in Enterprise Java
by Bruce Eckel et. Al.

Revision 1.1, 5-06-2003

Contents


Introduction to Enterprise Programming 6

Prerequisites. 7

Summary. 7

Network programming with Sockets & Channels 8

Identifying a machine. 8

Servers and clients 10

Testing programs without a network 10

Port: a unique place within the machine 11

Sockets. 11

A simple server and client 13

Serving multiple clients. 18

Datagrams. 23

Using URLs from within an applet. 23

Reading a file from the server 25

Selector Based Multiplexing in JDK1.4. 26

More to networking. 40

Exercises. 40

Remote Method Invocation (RMI) 42

Remote interfaces. 42

Implementing the remote interface. 43

Setting up the registry 44

Creating stubs and skeletons. 46

Using the remote object. 47

Summary. 48

Exercises. 48

Connecting to Databases 49

Getting the example to work. 52

Step 1: Find the JDBC Driver 52

Step 2: Configure the database 53

Step 3: Test the configuration 53

Step 4: Generate your SQL query 54

Step 5: Modify and paste in your query 55

A GUI version of the lookup program.. 55

Why the JDBC API seems so complex. 58

A more sophisticated example. 59

Summary. 66

Exercises. 66

Servlets 67

The basic servlet. 68

Servlets and multithreading. 71

Handling sessions with servlets. 72

The Cookie class 73

The Session class 73

Running the servlet examples. 76

Summary. 77

Exercises. 77

JavaServer Pages 78

Implicit objects. 79

JSP directives. 80

JSP scripting elements. 81

Extracting fields and values. 83

JSP page attributes and scope. 84

Manipulating sessions in JSP. 85

Creating and modifying cookies. 87

JSP summary. 88

Exercises. 88

Custom Tags 90

What do custom tags give us?. 91

Using Tags and JavaBeans. 98

Tags that manipulate their body content. 102

Tags that iterate. 106

Tags within Tags. 112

The Tag Classes. 113

Using TagExtraInfo classes. 115

Tag Library Descriptors Revisited. 116

Deploying Tag Libraries. 119

Using Third Party Tag Libraries. 121

Enterprise JavaBeans 123

Enterprise JavaBean Flavors. 126

EJB Roles. 129

The Basic APIs. 130

JNDI 130

EJB. 134

EJB Container internals. 136

Software. 138

The example application. 138

Your first Enterprise JavaBean. 144

Home Interface. 145

Component Interface. 147

Primary Key. 148

Implementation Class. 148

Deployment Descriptor. 153

Packaging. 155

Deployment. 156

Building it all 158

An EJB client application. 158

Simplifying EJB Development 162

Implementing a session bean. 167

EJB Local Interfaces. 175

Bulk accessors and value objects. 180

Finder Methods and EJB-QL. 183

Entity Relationships. 191

Summary. 208

Resources. 208

XML 210

What is XML?. 211

XML Elements. 215

XML Attributes. 215

Character Sets. 216

XML Technologies. 217

JAXP – Processing XML. 217

XML Namespaces. 217

Well-Formed and Valid XML. 218

Validating Parsers: SAX and DOM.. 220

SAX 221

DOM 223

Plus and Minus of SAX and DOM 226

XML Serialization. 226

Xerces Serialization. 226

DOM Level 3 Serialization. 228

XPath. 228

XML Transformations. 228

XML to HTML: Displaying a menu. 229

The Root Node. 230

XLink and XPointer. 234

Summary. 234




Introduction to Enterprise Programming

Historically, programming across multiple machines has been error-prone, difficult, and complex.

The programmer had to know many details about the network and sometimes even the hardware. You usually needed to understand the various “layers” of the networking protocol, and there were a lot of different functions in each different networking library concerned with connecting, packing, and unpacking blocks of information; shipping those blocks back and forth; and handshaking. It was a daunting task.

However, the basic idea of distributed computing is not so difficult, and is abstracted very nicely in the Java libraries. You want to:

·         Get some information from that machine over there and move it to this machine here, or vice versa. This is accomplished with basic network programming.

·         Connect to a database, which may live across a network. This is accomplished with Java DataBase Connectivity (JDBC), which is an abstraction away from the messy, platform-specific details of SQL (the structured query language used for most database transactions).

·         Provide services via a Web server. This is accomplished with Java’s servlets and JavaServer Pages (JSPs).

·         Execute methods on Java objects that live on remote machines transparently, as if those objects were resident on local machines. This is accomplished with Java’s Remote Method Invocation (RMI).

·         Use code written in other languages, running on other architectures. This is accomplished using the Extensible Markup Language (XML), which is directly supported by Java.

·         Isolate business logic from connectivity issues, especially connections with databases including transaction management and security. This is accomplished using Enterprise JavaBeans (EJBs). EJBs are not actually a distributed architecture, but the resulting applications are usually used in a networked client-server system.

·         Easily, dynamically, add and remove devices from a network representing a local system. This is accomplished with Java’s Jini.

Please note that each subject is voluminous and by itself the subject of entire books, so this book is only meant to familiarize you with the topics, not make you an expert (however, you can go a long way with the information presented here).

Prerequisites

This book assumes you have read (and understood most of) Thinking in Java, 3rd Edition (Prentice-Hall, 2003, available for download at www.MindView.net).

Summary

This chapter has introduced some, but not all, of the components that Sun refers to as J2EE: the Java 2 Enterprise Edition. The goal of J2EE is to create a set of tools that allows the Java developer to build server-based applications more quickly than before, and in a platform-independent way. It’s not only difficult and time-consuming to build such applications, but it’s especially hard to build them so that they can be easily ported to other platforms, and also to keep the business logic separated from the underlying details of the implementation. J2EE provides a framework to assist in creating server-based applications; these applications are in demand now, and that demand appears to be increasing.


Network programming with Sockets & Channels

One of Java’s great strengths is painless networking. The Java network library designers have made it quite similar to reading and writing files, except that the “file” exists on a remote machine and the remote machine can decide exactly what it wants to do about the information you’re requesting or sending. As much as possible, the underlying details of networking have been abstracted away and taken care of within the JVM and local machine installation of Java. The programming model you use is that of a file; in fact, you actually wrap the network connection (a “socket”) with stream objects, so you end up using the same method calls as you do with all other streams. In addition, Java’s built-in multithreading is exceptionally handy when dealing with another networking issue: handling multiple connections at once.

This section introduces Java’s networking support using easy-to-understand examples.

Identifying a machine

Of course, in order to tell one machine from another and to make sure that you are connected with a particular machine, there must be some way of uniquely identifying machines on a network. Early networks were satisfied to provide unique names for machines within the local network. However, Java works within the Internet, which requires a way to uniquely identify a machine from all the others in the world. This is accomplished with the IP (Internet Protocol) address which can exist in two forms“:

1.       The familiar DNS (Domain Name System) form. My domain name is bruceeckel.com, and if I have a computer called Opus in my domain, its domain name would be Opus.bruceeckel.com. This is exactly the kind of name that you use when you send email to people, and is often incorporated into a World Wide Web address.

2.      Alternatively, you can use the dotted quad” form, which is four numbers separated by dots, such as 123.255.28.120.

In both cases, the IP address is represented internally as a 32-bit number[1] (so each of the quad numbers cannot exceed 255), and you can get a special Java object to represent this number from either of the forms above by using the static InetAddress.getByName( ) method that’s in java.net. The result is an object of type InetAddress that you can use to build a “socket,” as you will see later.

As a simple example of using InetAddress.getByName( ), consider what happens if you have a dial-up Internet service provider (ISP). Each time you dial up, you are assigned a temporary IP address. But while you’re connected, your IP address has the same validity as any other IP address on the Internet. If someone connects to your machine using your IP address then they can connect to a Web server or FTP server that you have running on your machine. Of course, they need to know your IP address, and since a new one is assigned each time you dial up, how can you find out what it is?

The following program uses InetAddress.getByName( ) to produce your IP address. To use it, you must know the name of your computer. On Windows 95/98, go to “Settings,” “Control Panel,” “Network,” and then select the “Identification” tab. “Computer name” is the name to put on the command line.

//: c15:WhoAmI.java

// Finds out your network address when

// you're connected to the Internet.

// {RunByHand} Must be connected to the Internet

// {Args: www.google.com}

import java.net.*;

 

public class WhoAmI {

public static void main(String[] args)

throws Exception {

if(args.length != 1) {

System.err.println(

"Usage: WhoAmI MachineName");

System.exit(1);

}

InetAddress a =

InetAddress.getByName(args[0]);

System.out.println(a);

}

} ///:~

 

In this case, the machine is called “peppy.” So, once I’ve connected to my ISP I run the program:

java WhoAmI peppy

 

I get back a message like this (of course, the address is different each time):

peppy/199.190.87.75

 

If I tell my friend this address and I have a Web server running on my computer, he can connect to it by going to the URL http://199.190.87.75 (only as long as I continue to stay connected during that session). This can sometimes be a handy way to distribute information to someone else, or to test out a Web site configuration before posting it to a “real” server.

Servers and clients

The whole point of a network is to allow two machines to connect and talk to each other. Once the two machines have found each other they can have a nice, two-way conversation. But how do they find each other? It’s like getting lost in an amusement park: one machine has to stay in one place and listen while the other machine says, “Hey, where are you?”

The machine that “stays in one place” is called the server, and the one that seeks is called the client. This distinction is important only while the client is trying to connect to the server. Once they’ve connected, it becomes a two-way communication process and it doesn’t matter anymore that one happened to take the role of server and the other happened to take the role of the client.

So the job of the server is to listen for a connection, and that’s performed by the special server object that you create. The job of the client is to try to make a connection to a server, and this is performed by the special client object you create. Once the connection is made, you’ll see that at both server and client ends, the connection is magically turned into an I/O stream object, and from then on you can treat the connection as if you were reading from and writing to a file. Thus, after the connection is made you will just use the familiar I/O commands from Chapter 11. This is one of the nice features of Java networking.

Testing programs without a network

For many reasons, you might not have a client machine, a server machine, and a network available to test your programs. You might be performing exercises in a classroom situation, or you could be writing programs that aren’t yet stable enough to put onto the network. The creators of the Internet Protocol were aware of this issue, and they created a special address called localhost to be the “local loopback” IP address for testing without a network. The generic way to produce this address in Java is:

InetAddress addr = InetAddress.getByName(null);

 

If you hand getByName( ) a null, it defaults to using the localhost. The InetAddress is what you use to refer to the particular machine, and you must produce this before you can go any further. You can’t manipulate the contents of an InetAddress (but you can print them out, as you’ll see in the next example). The only way you can create an InetAddress is through one of that class’s overloaded static member methods getByName( ) (which is what you’ll usually use), getAllByName( ), or getLocalHost( ).

You can also produce the local loopback address by handing it the string localhost:

InetAddress.getByName("localhost");

 

(assuming “localhost” is configured in your machine’s “hosts” table), or by using its dotted quad form to name the reserved IP number for the loopback:

InetAddress.getByName("127.0.0.1");

 

All three forms produce the same result.

Port: a unique place
within the machine

An IP address isn’t enough to identify a unique server, since many servers can exist on one machine. Each IP machine also contains ports, and when you’re setting up a client or a server you must choose a port where both client and server agree to connect; if you’re meeting someone, the IP address is the neighborhood and the port is the bar.

The port is not a physical location in a machine, but a software abstraction (mainly for bookkeeping purposes). The client program knows how to connect to the machine via its IP address, but how does it connect to a desired service (potentially one of many on that machine)? That’s where the port numbers come in as a second level of addressing. The idea is that if you ask for a particular port, you’re requesting the service that’s associated with the port number. The time of day is a simple example of a service. Typically, each service is associated with a unique port number on a given server machine. It’s up to the client to know ahead of time which port number the desired service is running on.

The system services reserve the use of ports 1 through 1024, so you shouldn’t use those or any other port that you know to be in use. The first choice for examples in this book will be port 8080 (in memory of the venerable old 8-bit Intel 8080 chip in my first computer, a CP/M machine).

Sockets

The socket is the software abstraction used to represent the “terminals” of a connection between two machines. For a given connection, there’s a socket on each machine, and you can imagine a hypothetical “cable” running between the two machines with each end of the “cable” plugged into a socket. Of course, the physical hardware and cabling between machines is completely unknown. The whole point of the abstraction is that we don’t have to know more than is necessary.

In Java, you create a socket to make the connection to the other machine, then you get an InputStream and OutputStream (or, with the appropriate converters, Reader and Writer) from the socket in order to be able to treat the connection as an I/O stream object. There are two stream-based socket classes: a ServerSocket that a server uses to “listen” for incoming connections and a Socket that a client uses in order to initiate a connection. Once a client makes a socket connection, the ServerSocket returns (via the accept( ) method) a corresponding Socket through which communications will take place on the server side. From then on, you have a true Socket to Socket connection and you treat both ends the same way because they are the same. At this point, you use the methods getInputStream( ) and getOutputStream( ) to produce the corresponding InputStream and OutputStream objects from each Socket. These must be wrapped inside buffers and formatting classes just like any other stream object described in Chapter 11.

The use of the term ServerSocket would seem to be another example of a confusing naming scheme in the Java libraries. You might think ServerSocket would be better named “ServerConnector” or something without the word “Socket” in it. You might also think that ServerSocket and Socket should both be inherited from some common base class. Indeed, the two classes do have several methods in common, but not enough to give them a common base class. Instead, ServerSocket’s job is to wait until some other machine connects to it, then to return an actual Socket. This is why ServerSocket seems to be a bit misnamed, since its job isn’t really to be a socket but instead to make a Socket object when someone else connects to it.

However, the ServerSocket does create a physical “server” or listening socket on the host machine. This socket listens for incoming connections and then returns an “established” socket (with the local and remote endpoints defined) via the accept( ) method. The confusing part is that both of these sockets (listening and established) are associated with the same server socket. The listening socket can accept only new connection requests and not data packets. So while ServerSocket doesn’t make much sense programmatically, it does “physically.”

When you create a ServerSocket, you give it only a port number. You don’t have to give it an IP address because it’s already on the machine it represents. When you create a Socket, however, you must give both the IP address and the port number where you’re trying to connect. (However, the Socket that comes back from ServerSocket.accept( ) already contains all this information.)

A simple server and client

This example makes the simplest use of servers and clients using sockets. All the server does is wait for a connection, then uses the Socket produced by that connection to create an InputStream and OutputStream. These are converted to a Reader and a Writer, then wrapped in a BufferedReader and a PrintWriter. After that, everything it reads from the BufferedReader it echoes to the PrintWriter until it receives the line “END,” at which time it closes the connection.

The client makes the connection to the server, then creates an OutputStream and performs the same wrapping as in the server. Lines of text are sent through the resulting PrintWriter. The client also creates an InputStream (again, with appropriate conversions and wrapping) to hear what the server is saying (which, in this case, is just the words echoed back).

Both the server and client use the same port number and the client uses the local loopback address to connect to the server on the same machine so you don’t have to test it over a network. (For some configurations, you might need to be connected to a network for the programs to work, even if you aren’t communicating over that network.)

Here is the server:

//: c15:JabberServer.java

// Very simple server that just

// echoes whatever the client sends.

// {RunByHand}

import java.io.*;

import java.net.*;

 

public class JabberServer {

// Choose a port outside of the range 1-1024:

public static final int PORT = 8080;

public static void main(String[] args)

throws IOException {

ServerSocket s = new ServerSocket(PORT);

System.out.println("Started: " + s);

try {

// Blocks until a connection occurs:

Socket socket = s.accept();

try {

System.out.println(

"Connection accepted: "+ socket);

BufferedReader in =

new BufferedReader(

new InputStreamReader(

socket.getInputStream()));

// Output is automatically flushed

// by PrintWriter:

PrintWriter out =

new PrintWriter(

new BufferedWriter(

new OutputStreamWriter(

socket.getOutputStream())),true);

while (true) {

String str = in.readLine();

if (str.equals("END")) break;

System.out.println("Echoing: " + str);

out.println(str);

}

// Always close the two sockets...

} finally {

System.out.println("closing...");

socket.close();

}

} finally {

s.close();

}

}

} ///:~

 

You can see that the ServerSocket just needs a port number, not an IP address (since it’s running on this machine!). When you call accept( ), the method blocks until some client tries to connect to it. That is, it’s there waiting for a connection, but other processes can run (see Chapter 14). When a connection is made, accept( ) returns with a Socket object representing that connection.

The responsibility for cleaning up the sockets is crafted carefully here. If the ServerSocket constructor fails, the program just quits (notice we must assume that the constructor for ServerSocket doesn’t leave any open network sockets lying around if it fails). For this case, main( ) throws IOException so a try block is not necessary. If the ServerSocket constructor is successful then all other method calls must be guarded in a try-finally block to ensure that, no matter how the block is left, the ServerSocket is properly closed.

The same logic is used for the Socket returned by accept( ). If accept( ) fails, then we must assume that the Socket doesn’t exist or hold any resources, so it doesn’t need to be cleaned up. If it’s successful, however, the following statements must be in a try-finally block so that if they fail the Socket will still be cleaned up. Care is required here because sockets use important nonmemory resources, so you must be diligent in order to clean them up (since there is no destructor in Java to do it for you).

Both the ServerSocket and the Socket produced by accept( ) are printed to System.out. This means that their toString( ) methods are automatically called. These produce:

ServerSocket[addr=0.0.0.0,PORT=0,localport=8080]

Socket[addr=127.0.0.1,PORT=1077,localport=8080]

 

Shortly, you’ll see how these fit together with what the client is doing.

The next part of the program looks just like opening files for reading and writing except that the InputStream and OutputStream are created from the Socket object. Both the InputStream and OutputStream objects are converted to Reader and Writer objects using the “converter” classes InputStreamReader and OutputStreamWriter, respectively. You could also have used the Java 1.0 InputStream and OutputStream classes directly, but with output there’s a distinct advantage to using the Writer approach. This appears with PrintWriter, which has an overloaded constructor that takes a second argument, a boolean flag that indicates whether to automatically flush the output at the end of each println( ) (but not print( )) statement. Every time you write to out, its buffer must be flushed so the information goes out over the network. Flushing is important for this particular example because the client and server each wait for a line from the other party before proceeding. If flushing doesn’t occur, the information will not be put onto the network until the buffer is full, which causes lots of problems in this example.

When writing network programs you need to be careful about using automatic flushing. Every time you flush the buffer a packet must be created and sent. In this case, that’s exactly what we want, since if the packet containing the line isn’t sent then the handshaking back and forth between server and client will stop. Put another way, the end of a line is the end of a message. But in many cases, messages aren’t delimited by lines so it’s much more efficient to not use auto flushing and instead let the built-in buffering decide when to build and send a packet. This way, larger packets can be sent and the process will be faster.

Note that, like virtually all streams you open, these are buffered. There’s an exercise at the end of this chapter to show you what happens if you don’t buffer the streams (things get slow).

The infinite while loop reads lines from the BufferedReader in and writes information to System.out and to the PrintWriter out. Note that in and out could be any streams, they just happen to be connected to the network.

When the client sends the line consisting of “END,” the program breaks out of the loop and closes the Socket.

Here’s the client:

//: c15:JabberClient.java

// Very simple client that just sends

// lines to the server and reads lines

// that the server sends.

// {RunByHand}

import java.net.*;

import java.io.*;

 

public class JabberClient {

public static void main(String[] args)

throws IOException {

// Passing null to getByName() produces the

// special "Local Loopback" IP address, for

// testing on one machine w/o a network:

InetAddress addr =

InetAddress.getByName(null);

// Alternatively, you can use

// the address or name:

// InetAddress addr =

// InetAddress.getByName("127.0.0.1");

// InetAddress addr =

// InetAddress.getByName("localhost");

System.out.println("addr = " + addr);

Socket socket =

new Socket(addr, JabberServer.PORT);

// Guard everything in a try-finally to make

// sure that the socket is closed:

try {

System.out.println("socket = " + socket);

BufferedReader in =

new BufferedReader(

new InputStreamReader(

socket.getInputStream()));

// Output is automatically flushed

// by PrintWriter:

PrintWriter out =

new PrintWriter(

new BufferedWriter(

new OutputStreamWriter(

socket.getOutputStream())),true);

for(int i = 0; i < 10; i ++) {

out.println("howdy " + i);

String str = in.readLine();

System.out.println(str);

}

out.println("END");

} finally {

System.out.println("closing...");

socket.close();

}

}

} ///:~

 

In main( ) you can see all three ways to produce the InetAddress of the local loopback IP address: using null, localhost, or the explicit reserved address 127.0.0.1. Of course, if you want to connect to a machine across a network you substitute that machine’s IP address. When the InetAddress addr is printed (via the automatic call to its toString( ) method) the result is:

localhost/127.0.0.1

 

By handing getByName( ) a null, it defaulted to finding the localhost, and that produced the special address 127.0.0.1.

Note that the Socket called socket is created with both the InetAddress and the port number. To understand what it means when you print one of these Socket objects, remember that an Internet connection is determined uniquely by these four pieces of data: clientHost, clientPortNumber, serverHost, and serverPortNumber. When the server comes up, it takes up its assigned port (8080) on the localhost (127.0.0.1). When the client comes up, it is allocated to the next available port on its machine, 1077 in this case, which also happens to be on the same machine (127.0.0.1) as the server. Now, in order for data to move between the client and server, each side has to know where to send it. Therefore, during the process of connecting to the “known” server, the client sends a “return address” so the server knows where to send its data. This is what you see in the example output for the server side:

Socket[addr=127.0.0.1,port=1077,localport=8080]

 

This means that the server just accepted a connection from 127.0.0.1 on port 1077 while listening on its local port (8080). On the client side:

Socket[addr=localhost/127.0.0.1,PORT=8080,localport=1077]

 

which means that the client made a connection to 127.0.0.1 on port 8080 using the local port 1077.

You’ll notice that every time you start up the client anew, the local port number is incremented. It starts at 1025 (one past the reserved block of ports) and keeps going up until you reboot the machine, at which point it starts at 1025 again. (On UNIX machines, once the upper limit of the socket range is reached, the numbers will wrap around to the lowest available number again.)

Once the Socket object has been created, the process of turning it into a BufferedReader and PrintWriter is the same as in the server (again, in both cases you start with a Socket). Here, the client initiates the conversation by sending the string “howdy” followed by a number. Note that the buffer must again be flushed (which happens automatically via the second argument to the PrintWriter constructor). If the buffer isn’t flushed, the whole conversation will hang because the initial “howdy” will never get sent (the buffer isn’t full enough to cause the send to happen automatically). Each line that is sent back from the server is written to System.out to verify that everything is working correctly. To terminate the conversation, the agreed-upon “END” is sent. If the client simply hangs up, then the server throws an exception.

You can see that the same care is taken here to ensure that the network resources represented by the Socket are properly cleaned up, using a try-finally block.

Sockets produce a “dedicated” connection that persists until it is explicitly disconnected. (The dedicated connection can still be disconnected unexplicitly if one side, or an intermediary link, of the connection crashes.) This means the two parties are locked in communication and the connection is constantly open. This seems like a logical approach to networking, but it puts an extra load on the network. Later in this chapter you’ll see a different approach to networking, in which the connections are only temporary.

Serving multiple clients

The JabberServer works, but it can handle only one client at a time. In a typical server, you’ll want to be able to deal with many clients at once. The answer is multithreading, and in languages that don’t directly support multithreading this means all sorts of complications. In Chapter 14 you saw that multithreading in Java is about as simple as possible, considering that multithreading is a rather complex topic. Because threading in Java is reasonably straightforward, making a server that handles multiple clients is relatively easy.

The basic scheme is to make a single ServerSocket in the server and call accept( ) to wait for a new connection. When accept( ) returns, you take the resulting Socket and use it to create a new thread whose job is to serve that particular client. Then you call accept( ) again to wait for a new client.

In the following server code, you can see that it looks similar to the JabberServer.java example except that all of the operations to serve a particular client have been moved inside a separate thread class:

//: c15:MultiJabberServer.java

// A server that uses multithreading

// to handle any number of clients.

// {RunByHand}

import java.io.*;

import java.net.*;

 

class ServeOneJabber extends Thread {

private Socket socket;

private BufferedReader in;

private PrintWriter out;

public ServeOneJabber(Socket s)

throws IOException {

socket = s;

in =

new BufferedReader(

new InputStreamReader(

socket.getInputStream()));

// Enable auto-flush:

out =

new PrintWriter(

new BufferedWriter(

new OutputStreamWriter(

socket.getOutputStream())), true);

// If any of the above calls throw an

// exception, the caller is responsible for

// closing the socket. Otherwise the thread

// will close it.

start(); // Calls run()

}

public void run() {

try {

while (true) {

String str = in.readLine();

if (str.equals("END")) break;

System.out.println("Echoing: " + str);

out.println(str);

}

System.out.println("closing...");

} catch(IOException e) {

System.err.println("IO Exception");

} finally {

try {

socket.close();

} catch(IOException e) {

System.err.println("Socket not closed");

}

}

}

}

 

public class MultiJabberServer {

static final int PORT = 8080;

public static void main(String[] args)

throws IOException {

ServerSocket s = new ServerSocket(PORT);

System.out.println("Server Started");

try {

while(true) {

// Blocks until a connection occurs:

Socket socket = s.accept();

try {

new ServeOneJabber(socket);

} catch(IOException e) {

// If it fails, close the socket,

// otherwise the thread will close it:

socket.close();

}

}

} finally {

s.close();

}

}

} ///:~

 

The ServeOneJabber thread takes the Socket object that’s produced by accept( ) in main( ) every time a new client makes a connection. Then, as before, it creates a BufferedReader and auto-flushed PrintWriter object using the Socket. Finally, it calls the special Thread method start( ), which performs thread initialization and then calls run( ). This performs the same kind of action as in the previous example: reading something from the socket and then echoing it back until it reads the special “END” signal.

The responsibility for cleaning up the socket must again be carefully designed. In this case, the socket is created outside of the ServeOneJabber so the responsibility can be shared. If the ServeOneJabber constructor fails, it will just throw the exception to the caller, who will then clean up the thread. But if the constructor succeeds, then the ServeOneJabber object takes over responsibility for cleaning up the thread, in its run( ).

Notice the simplicity of the MultiJabberServer. As before, a ServerSocket is created and accept( ) is called to allow a new connection. But this time, the return value of accept( ) (a Socket) is passed to the constructor for ServeOneJabber, which creates a new thread to handle that connection. When the connection is terminated, the thread simply goes away.

If the creation of the ServerSocket fails, the exception is again thrown through main( ). But if the creation succeeds, the outer try-finally guarantees its cleanup. The inner try-catch guards only against the failure of the ServeOneJabber constructor; if the constructor succeeds, then the ServeOneJabber thread will close the associated socket.

To test that the server really does handle multiple clients, the following program creates many clients (using threads) that connect to the same server. The maximum number of threads allowed is determined by the final int MAX_THREADS.

//: c15:MultiJabberClient.java

// Client that tests the MultiJabberServer

// by starting up multiple clients.

// {RunByHand}

import java.net.*;

import java.io.*;

 

class JabberClientThread extends Thread {

private Socket socket;

private BufferedReader in;

private PrintWriter out;

private static int counter = 0;

private int id = counter++;

private static int threadcount = 0;

public static int threadCount() {

return threadcount;

}

public JabberClientThread(InetAddress addr) {

System.out.println("Making client " + id);

threadcount++;

try {

socket =

new Socket(addr, MultiJabberServer.PORT);

} catch(IOException e) {

System.err.println("Socket failed");

// If the creation of the socket fails,

// nothing needs to be cleaned up.

}

try {

in =

new BufferedReader(

new InputStreamReader(

socket.getInputStream()));

// Enable auto-flush:

out =

new PrintWriter(

new BufferedWriter(

new OutputStreamWriter(

socket.getOutputStream())), true);

start();

} catch(IOException e) {

// The socket should be closed on any

// failures other than the socket

// constructor:

try {

socket.close();

} catch(IOException e2) {

System.err.println("Socket not closed");

}

}

// Otherwise the socket will be closed by

// the run() method of the thread.

}

public void run() {

try {

for(int i = 0; i < 25; i++) {

out.println("Client " + id + ": " + i);

String str = in.readLine();

System.out.println(str);

}

out.println("END");

} catch(IOException e) {

System.err.println("IO Exception");

} finally {

// Always close it:

try {

socket.close();

} catch(IOException e) {

System.err.println("Socket not closed");

}

threadcount--; // Ending this thread

}

}

}

 

public class MultiJabberClient {

static final int MAX_THREADS = 40;

public static void main(String[] args)

throws IOException, InterruptedException {

InetAddress addr =

InetAddress.getByName(null);

while(true) {

if(JabberClientThread.threadCount()

< MAX_THREADS)

new JabberClientThread(addr);

Thread.currentThread().sleep(100);

}

}

} ///:~

 

The JabberClientThread constructor takes an InetAddress and uses it to open a Socket. You’re probably starting to see the pattern: the Socket is always used to create some kind of Reader and/or Writer (or InputStream and/or OutputStream) object, which is the only way that the Socket can be used. (You can, of course, write a class or two to automate this process instead of doing all the typing if it becomes painful.) Again, start( ) performs thread initialization and calls run( ). Here, messages are sent to the server and information from the server is echoed to the screen. However, the thread has a limited lifetime and eventually completes. Note that the socket is cleaned up if the constructor fails after the socket is created but before the constructor completes. Otherwise the responsibility for calling close( ) for the socket is relegated to the run( ) method.

The threadcount keeps track of how many JabberClientThread objects currently exist. It is incremented as part of the constructor and decremented as run( ) exits (which means the thread is terminating). In MultiJabberClient.main( ), you can see that the number of threads is tested, and if there are too many, no more are created. Then the method sleeps. This way, some threads will eventually terminate and more can be created. You can experiment with MAX_THREADS to see where your particular system begins to have trouble with too many connections.

Datagrams

The examples you’ve seen so far use the Transmission Control Protocol (TCP, also known as stream-based sockets), which is designed for ultimate reliability and guarantees that the data will get there. It allows retransmission of lost data, it provides multiple paths through different routers in case one goes down, and bytes are delivered in the order they are sent. All this control and reliability comes at a cost: TCP has a high overhead.

There’s a second protocol, called User Datagram Protocol (UDP), which doesn’t guarantee that the packets will be delivered and doesn’t guarantee that they will arrive in the order they were sent. It’s called an “unreliable protocol” (TCP is a “reliable protocol”), which sounds bad, but because it’s much faster it can be useful. There are some applications, such as an audio signal, in which it isn’t so critical if a few packets are dropped here or there but speed is vital. Or consider a time-of-day server, where it really doesn’t matter if one of the messages is lost. Also, some applications might be able to fire off a UDP message to a server and can then assume, if there is no response in a reasonable period of time, that the message was lost.

Typically, you’ll do most of your direct network programming with TCP, and only occasionally will you use UDP. There’s a more complete treatment of UDP, including an example, in the first edition of this book (available on the CD ROM bound into this book, or as a free download from www.BruceEckel.com).

Using URLs from within an applet

It’s possible for an applet to cause the display of any URL through the Web browser the applet is running within. You can do this with the following line:

getAppletContext().showDocument(u);

 

in which u is the URL object. Here’s a simple example that redirects you to another Web page. Although you’re just redirected to an HTML page, you could also redirect to the output of a CGI program.

//: c15:ShowHTML.java

// <applet code=ShowHTML width=100 height=50>

// </applet>

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.net.*;

import java.io.*;

import com.bruceeckel.swing.*;

 

public class ShowHTML extends JApplet {

JButton send = new JButton("Go");

JLabel l = new JLabel();

public void init() {

Container cp = getContentPane();

cp.setLayout(new FlowLayout());

send.addActionListener(new Al());

cp.add(send);

cp.add(l);

}

class Al implements ActionListener {

public void actionPerformed(ActionEvent ae) {

try {

// This could be a CGI program instead of

// an HTML page.

URL u = new URL(getDocumentBase(),

"FetcherFrame.html");

// Display the output of the URL using

// the Web browser, as an ordinary page:

getAppletContext().showDocument(u);

} catch(Exception e) {

l.setText(e.toString());

}

}

}

public static void main(String[] args) {

Console.run(new ShowHTML(), 100, 50);

}

} ///:~

 

The beauty of the URL class is how much it shields you from. You can connect to Web servers without knowing much at all about what’s going on under the covers.

Reading a file from the server

A variation on the above program reads a file located on the server. In this case, the file is specified by the client:

//: c15:Fetcher.java

// <applet code=Fetcher width=500 height=300>

// </applet>

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import java.net.*;

import java.io.*;

import com.bruceeckel.swing.*;

 

public class Fetcher extends JApplet {

JButton fetchIt= new JButton("Fetch the Data");

JTextField f =

new JTextField("Fetcher.java", 20);

JTextArea t = new JTextArea(10,40);

public void init() {

Container cp = getContentPane();

cp.setLayout(new FlowLayout());

fetchIt.addActionListener(new FetchL());

cp.add(new JScrollPane(t));

cp.add(f); cp.add(fetchIt);

}

public class FetchL implements ActionListener {

public void actionPerformed(ActionEvent e) {

try {

URL url = new URL(getDocumentBase(),

f.getText());

t.setText(url + "\n");

InputStream is = url.openStream();

BufferedReader in = new BufferedReader(

new InputStreamReader(is));

String line;

while ((line = in.readLine()) != null)

t.append(line + "\n");

} catch(Exception ex) {

t.append(ex.toString());

}

}

}

public static void main(String[] args) {

Console.run(new Fetcher(), 500, 300);

}

} ///:~

 

The creation of the URL object is similar to the previous example—getDocumentBase( ) is the starting point as before, but this time the name of the file is read from the JTextField. Once the URL object is created, its String version is placed in the JTextArea so we can see what it looks like. Then an InputStream is procured from the URL, which in this case will simply produce a stream of the characters in the file. After converting to a Reader and buffering, each line is read and appended to the JTextArea. Note that the JTextArea has been placed inside a JScrollPane so that scrolling is handled automatically.

Selector Based Multiplexing in JDK1.4

When you are reading or writing to a socket, you need to make the transfer of data efficient. Let us consider the write operation first. When you write data at the application layer (TCP or UDP socket), you are writing data into the operating system buffer. This data eventually forms the payload of a (TCP or UDP) packet that needs to be transferred to the destination machine through the network. When you write to the socket and if there isn't enough room available in the buffer, the write call will block. If you read from a socket and there isn't enough information to read from the operating system buffer that has the data received from the network, the read call will block. If a thread blocks on a read or write operation, that thread is unable to do anything else and may be slowing the performance of your program. Prior to JDK1.4 there was no way to break this thread from the blocked state. With channels you can perform an asynchronous close operation on the channel and the thread blocked on this channel recieves the AsynchronousCloseException.

The asynchronous IO in Java achieves the same thing as achieved by the select() call on unix systems. You can give a list of descriptors (readable or writable) to the select() function and it monitors these descriptors for some change of event. For a descriptor representing a socket you are reading from, data in the operating systems buffer for this descriptor represents event. For a descriptor representing a socket you are writing to, availability of space to write in the internal operating system buffer for this socket represents an event. Hence the select() call watches multiple descriptors to check for events.

What if you just read and write to descriptors whenever you want? Select can handle multiple descriptors thus allowing you to monitor multiple sockets. Consider an example of a chat server where the server has a connection to various clients. The type of data arriving at the server is intermittent. The server is suppose to read data from the sockets and flash it on a GUI that is shown to every client - to achieve this you read data from every client and write this data to every other client. Consider 5 clients 1, 2, 3, 4, and 5. If the server was programmed to perform a read on 1, write on 2, 3, 4, and 5, next read on 2 and write on 1, 3, 4, 5 and so on, then it may so happen that while the server thread is blocked on read on one of the client sockets, there may be data available from other sockets. One solution would be to allocate a different thread for each of the clients (pre JDK1.4). But this would not be scalable. Instead you can have a selector based mechanism which watches all the client sockets. It knows which socket has data that can be read without blocking. But if a single server thread does all this work (selection and write on each client) it would not be responsive. Hence in such a situation one thread monitors the sockets for read, selects which socket can be read, and delegates other responsibility (writing to other clients) to another thread(s) or a thread pool.

This pattern is called the reactor pattern where events are decoupled from the action associated with events (Pattern Oriented Software Architecture - Doug Schmidt).

In JDK 1.4 you create a channel, register a Selector object with the channel that will watch the channel for events. Many channels register with the same Selector object. A single thread that calls the Selector.select(), watches multiple channels. The classes ServerSocket, Socket and DatagramSocket, each have a getChannel() method but it returns null except if a channel was created using the open() call (DatagramChannel.open(), SocketChannel.open(), ServerSocketChannel.open()). You then need to associate a socket with this channel.

You multiplex several channels (hence sockets) using a Selector. The static call Selector.select() blocks for an event to occur on one of the channels. There is also a non blocking version to this method that takes, the number of milliseconds to sleep or block before it returns.

ByteBuffer is used to copy data from and into a channel. ByteBuffer is a stream of octets and you have to decode this stream as characters. At the client end in MultiJabberClient.java this was done using Writer and OuputStreamWriter classes. These classes converted the characters into a stream of bytes.

The program below NonBlockingIO.java explains how you can used Selector and Channel to do the multiplexing. This program needs Server running. It causes an exception at server, but its aim is not to communicate with the server but to show how select() works.

//: TIEJ:X1:NonBlockingIO.java
// Socket and selector configuration for non-blocking
// Connects to JabberServer.java
// {RunByHand}
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.io.*;
/**
* Aim: Shows how to use selector. No reading/writing
* just shows the readiness of operations.
*
* PseudoCode:
* -> Create a selector.
* -> Create a channel
* -> Bind the socket associated with this channel to a
* <client-port>
* -> Configure the channel as non-blocking
* -> Register the channel with selector.
* -> Invoke select() so that it blocks until registered
* channel is ready. (as opposed to select(long timeout)
* -> Get the set of keys whose underlying channel is ready
* for the operation they showed interest when they
* registered with the selector.
* -> Iterate through the keys.
* -> For every key check if the underlying channel is ready
* for the operation it is interested in.
* -> If ready print message of readiness.
*
* Notes:
* -> Need MultiJabberServer running on the local machine.
* You run it to connect to the local MultiJabberServer
* -> It causes and exception at MultiJabberServer but
* this exception is expected.
*/
public class NonBlockingIO {
public static void main(String[] args)
throws IOException {
if(args.length < 2) {
System.out.println(
"Usage: java <client port> <local server port>");
System.exit(1);
}
int cPort = Integer.parseInt(args[0]);
int sPort = Integer.parseInt(args[1]);
SocketChannel ch = SocketChannel.open();
Selector sel = sel = Selector.open();
try {
ch.socket().bind(new InetSocketAddress(cPort));
ch.configureBlocking(false);
// channel interested in performing read/write/connect
ch.register(sel, SelectionKey.OP_READ
| SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT);
// Unblocks when ready to read/write/connect
sel.select();
// Keys whose underlying channel is ready, the
// operation this channel is interested in can be
// performed without blocking.
Iterator it = sel.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
it.remove();
// Is underlying channel of key ready to connect?
// if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
if(key.isConnectable()) {
InetAddress ad = InetAddress.getLocalHost();
System.out.println("Connect will not block");
//You must check the return value of connect to make
//sure that it has connected. This call being
//non-blocking may return without connecting when
//there is no server where you are trying to connect
//Hence you call finishConnect() that finishes the
//connect operation.
if(!ch.connect(new InetSocketAddress(ad, sPort)))
ch.finishConnect();
}
// Is underlying channel of key ready to read?
// if((key.readyOps() & SelectionKey.OP_READ) != 0)
if(key.isReadable())
System.out.println("Read will not block");
// Is underlying channel of key ready to write?
// if((key.readyOps() & SelectionKey.OP_WRITE) != 0)
if(key.isWritable())
System.out.println("Write will not block");
}
} finally {
ch.close();
sel.close();
}
}
} ///:~

 

As mentioned above you need to create a channel using the open() call. SocketChannel.open() creates a channel. Since it extends from AbstractSelectableChannel (DatagramChannel and SocketChannel) it has the functionality for registering itself to a selector. The register call does this. It takes as an argument the Selector with to register the channel with and the events that are of interest to this channel. Here the SocketChannel is shown to be interested in connect, read and write - hence SelectionKey.OP_CONNECT,  SelectionKey.OP_READ and SelectionKey.OP_WRITE are specified in the register call while registering the channel with the Selector.

The static call Selector.select() watches all the channels that are registered with it for the events they registered for (second argument to register). You can have a channel interested in more than one event.

The next is an example that works like the JabberClient1.java but uses Selector.

//: TIEJ:X1:JabberClient1.java
// Very simple client that just sends lines to the server
// and reads lines that the server sends.
// {RunByHand}
import java.net.*;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JabberClient1 {
public static void main(String[] args)
throws IOException {
if(args.length < 1) {
System.out.println(
"Usage: java JabberClient1 <client-port>");
System.exit(1);
}
int clPrt = Integer.parseInt(args[0]);
SocketChannel sc = SocketChannel.open();
Selector sel = Selector.open();
try {
sc.configureBlocking(false);
sc.socket().bind(new InetSocketAddress(clPrt));
sc.register(sel, SelectionKey.OP_READ |
SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT);
int i = 0;
// Because of the asynchronous nature you do not know
// when reading and writing is done, hence you need to
// keep track of this, boolean written is used to
// alternate between read and write. Whatever is written
// is echoed and should be read.
// boolean done is used to check when to break out of
// the loop
boolean written = false, done = false;
//JabberServer.java to which this client connects writes
//using BufferedWriter.println(). This method performs
//encoding according to the defualt charset
String encoding = System.getProperty("file.encoding");
Charset cs = Charset.forName(encoding);
ByteBuffer buf = ByteBuffer.allocate(16);
while(!done) {
sel.select();
Iterator it = sel.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
it.remove();
sc = (SocketChannel)key.channel();
if(key.isConnectable() && !sc.isConnected()) {
InetAddress addr = InetAddress.getByName(null);
boolean success = sc.connect(
new InetSocketAddress(addr,
JabberServer.PORT));
if(!success) sc.finishConnect();
}
if(key.isReadable() && written) {
if(sc.read((ByteBuffer)buf.clear()) > 0) {
written = false;
String response = cs.decode(
(ByteBuffer) buf.flip()).toString();
System.out.print(response);
if(response.indexOf("END") != -1) done = true;
}
}
if(key.isWritable() && !written) {
if(i < 10) sc.write(ByteBuffer.wrap(
new String("howdy "+ i + '\n').getBytes()));
else if(i == 10) sc.write(ByteBuffer.wrap(
new String("END\n").getBytes()));
written = true;
i++;
}
}
}
} finally {
sc.close();
sel.close();
}
}
} ///:~

 

The next example below shows a simple selector based mechanism for the MultiJabberServer discussed earlier. This server works the same way as the old one did but is more efficient in that it does not need a separate thread to handle each client.

//: TIEJ:X1:MultiJabberServer1.java
// Has the same semantics as multi-threaded
// MultiJabberServer
// {RunByHand}
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;
/**
* The Server accepts connections in non-blocking fashion.
* A connection when established, a socket is created,
* which is registered for read/write with the selector.
* Reading/Writing is performed on this socket when the
* selector unblocks.
* This program works exactly the same way as MultiJabberServer.
*/
public class MultiJabberServer1 {
public static final int PORT = 8080;
public static void main(String[] args)
throws IOException {
//Channel read from data will be in ByteBuffer form
//written by PrintWriter.println(). Decoding of this
//byte stream requires character set of default encoding.
String encoding = System.getProperty("file.encoding");
//Had to initialized here since we do not wish to create
//a new instance of Charset everytime it is required
//Charset cs = Charset.forName(
// System.getProperty("file.encoding"));
Charset cs = Charset.forName(encoding);
ByteBuffer buffer = ByteBuffer.allocate(16);
SocketChannel ch = null;
ServerSocketChannel ssc = ServerSocketChannel.open();
Selector sel = Selector.open();
try {
ssc.configureBlocking(false);
//Local address on which it will listen for connections
//Note: Socket.getChannel() returns null unless a channel
//is associated with it as shown below.
//i.e the expression (ssc.socket().getChannel() != null) is true
ssc.socket().bind(new InetSocketAddress(PORT));
// Channel is interested in OP_ACCEPT events
SelectionKey key =
ssc.register(sel, SelectionKey.OP_ACCEPT);
System.out.println("Server on port: " + PORT);
while(true) {
sel.select();
Iterator it = sel.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey skey = (SelectionKey)it.next();
it.remove();
if(skey.isAcceptable()) {
ch = ssc.accept();
System.out.println(
"Accepted connection from:" + ch.socket());
ch.configureBlocking(false);
ch.register(sel, SelectionKey.OP_READ);
} else {
// Note no check performed if the channel
// is writable or readable - to keep it simple
ch = (SocketChannel)skey.channel();
ch.read(buffer);
CharBuffer cb = cs.decode(
(ByteBuffer)buffer.flip());
String response = cb.toString();
System.out.print("Echoing : " + response);
ch.write((ByteBuffer)buffer.rewind());
if(response.indexOf("END") != -1) ch.close();
buffer.clear();
}
}
}
} finally {
if(ch != null) ch.close();
ssc.close();
sel.close();
}
}
} ///:~

 

Here is a simple implementation of a Thread Pool. There are no polling (busy-wait) threads in this implementation. It is completely based on wait() and notify().

//: TIEJ:X1:Worker.java
// Instances of Worker are pooled in threadpool
// {Clean: WorkerErr.log, WorkerErr.log.lck}
// {RunByHand}
import java.io.*;
import java.util.logging.*;
public class Worker extends Thread {
public static final Logger logger =
Logger.getLogger("Worker");
private String workerId;
private Runnable task;
// Needs a reference of threadpool in which it exists so
// that it can add itself to this threadpool when done.
private ThreadPool threadpool;
static {
try {
logger.setUseParentHandlers(false);
FileHandler ferr = new FileHandler("WorkerErr.log");
ferr.setFormatter(new SimpleFormatter());
logger.addHandler(ferr);
} catch(IOException e) {
System.out.println("Logger not initialized..");
}
}
public Worker(String id, ThreadPool pool) {
workerId = id;
threadpool = pool;
start();
}
//ThreadPool when schedules a task uses this method
//to delegate task to a Worker thread. In addition to setting
//the task (of type Runnable) it also triggers the waiting
//run() method to start executing the task.
public void setTask(Runnable t) {
task = t;
synchronized(this) {
notify();
}
}
public void run() {
try {
while(!threadpool.isStopped()) {
synchronized(this) {
if(task != null) {
try {
task.run(); // run the task
} catch(Exception e) {
logger.log(Level.SEVERE,
"Exception in source Runnable task", e);
}
// return itself to threadpool
threadpool.putWorker(this);
}
wait();
}
}
System.out.println(this + " Stopped");
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
}
public String toString() {
return "Worker : " + workerId;
}
} ///:~

 

This the basic algorithm:
while true:

1.check the queue of tasks

2.if empty, wait on this queue till a task gets added

(addTask() call that adds a task will notify this queue to unblock)

3.Try to get a worker thread from the thread pool.

4.If none is available, wait on the thread pool.
(When there is a free thread available notify this threadpool to unblock)

5.At this point there is a task in the queue and also a free worker thread

6.Delegate task from the queue to a worker thread.

end while:

//: TIEJ:X1:ThreadPool.java

// Thread that polls and executes tasks in pool.

// {RunByHand}

import java.util.*;

public class ThreadPool extends Thread {

private static final int DEFAULT_NUM_WORKERS = 5;

private LinkedList

workerPool = new LinkedList(),

taskList = new LinkedList();

private boolean stopped = false;

public ThreadPool() {

this(DEFAULT_NUM_WORKERS);

}

public ThreadPool(int numOfWorkers) {

for(int i = 0; i < numOfWorkers; i++)

workerPool.add(new Worker("" + i, this));

start();

}

public void run() {

try {

while(!stopped) {

if(taskList.isEmpty()) {

synchronized(taskQueue) {

// If queue is empty, wait for tasks to be added

taskList.wait();

}

} else if(workerPool.isEmpty()) {

synchronized(workerPool) {

// If no worker threads available, wait till

// one is available

workerPool.wait();

}

}

// Run the next task from the tasks scheduled

getWorker().setTask(

(Runnable)taskList.removeLast());

}

} catch(InterruptedException e) {

throw new RuntimeException(e);

}

}

public void addTask(Runnable task) {

taskList.addFirst(task);

synchronized(taskList) {

taskList.notify(); // If new task added, notify

}

}

public void putWorker(Worker worker) {

workerPool.addFirst(worker);

//There may be cases when you have a pool of 5 threads

//and the requirement exceeds this. That is when a Worker is required

//but none is available (or free), it just blocks on threadpool.

//This is the event that there is now a free Worker thread in

//threadpool. Hence this thread does a notification that unblocks

//the ThreadPool thread waiting on threadpool

synchronized(workerPool) {

workerPool.notify();

}

}

private Worker getWorker() {

return (Worker)workerPool.removeLast();

}

public boolean isStopped() {

return stopped;

}

public void stopThreads() {

stopped = true;

Iterator it = workerPool.iterator();

while(it.hasNext()) {

Worker w = (Worker)it.next();

synchronized(w) {

w.notify();

}

}

} // Junit test

public void testThreadPool() {

ThreadPool tp = new ThreadPool();

for(int i = 0; i < 10; i++) {

tp.addTask(new Runnable() {

public void run() {

System.out.println("A");

}

});

}

tp.stopThreads();

}

} ///:~

 

Next is MultiJabberServer2.java that uses threadpool. This is the Reactor pattern. As stated above, the events are decoupled from their associated actions. The threadpool asynchronously decouples the actions associated with the events. In an enterprise system this decoupling is typically achieved using Java Messaging System.(JMS)

//: TIEJ:X1:MultiJabberServer2.java

// Same semantics as MultiJabberServer1 using thread pooling.

// {RunByHand}

import java.io.*;

import java.net.*;

import java.nio.*;

import java.nio.channels.*;

import java.nio.charset.*;

import java.util.*;

 

class ServeOneJabber implements Runnable {

private SocketChannel channel;

private Selector sel;

public ServeOneJabber(SocketChannel ch)

throws IOException {

channel = ch;

sel = Selector.open();

}

public void run() {

ByteBuffer buffer = ByteBuffer.allocate(16);

boolean read = false, done = false;

String response = null;

try {

channel.register(sel, SelectionKey.OP_READ |

SelectionKey.OP_WRITE);

while(!done) {

sel.select();

Iterator it = sel.selectedKeys().iterator();

while(it.hasNext()) {

SelectionKey key = (SelectionKey) it.next();

it.remove();

if(key.isReadable() && !read) {

if(channel.read(buffer) > 0) read = true;

CharBuffer cb = MultiJabberServer2.CS.decode(

(ByteBuffer)buffer.flip());

response = cb.toString();

}

if(key.isWritable() && read) {

System.out.print("Echoing : " + response);

channel.write((ByteBuffer)buffer.rewind());

if(response.indexOf("END") != -1) done = true;

buffer.clear();

read = false;

}

}

}

} catch(IOException e) {

// will be caught by Worker.java and logged.

// Need to throw runtime exception since we cannot

// keep it as IOException

throw new RuntimeException(e);

} finally {

try {

channel.close();

} catch(IOException e) {

System.out.println("Channel not closed.");

// Throw it so that worker thread can log it.

throw new RuntimeException(e);

}

}

}

}

 

public class MultiJabberServer2 {

public static final int PORT = 8080;

private static String encoding =

System.getProperty("file.encoding");

public static final Charset CS =

Charset.forName(encoding);

// Make thread pool with 20 Worker threads.

private static ThreadPool pool = new ThreadPool(20);

public static void main(String[] args)

throws IOException {

ServerSocketChannel ssc = ServerSocketChannel.open();

Selector sel = Selector.open();

try {

ssc.configureBlocking(false);

ssc.socket().bind(new InetSocketAddress(PORT));

SelectionKey key =

ssc.register(sel, SelectionKey.OP_ACCEPT);

System.out.println("Server on port: " + PORT);

while(true) {

sel.select();

Iterator it = sel.selectedKeys().iterator();

while(it.hasNext()) {

SelectionKey skey = (SelectionKey) it.next();

it.remove();

if(skey.isAcceptable()) {

SocketChannel channel = ssc.accept();

System.out.println("Accepted connection from:" +

channel.socket());

channel.configureBlocking(false);

// Decouple event and associated action

pool.addTask(new ServeOneJabber(channel));

}

}

}

} finally {

ssc.close();

sel.close();

}

}

} ///:~

 

This is a minor update to JabberServer.java. Initially when a client sent 'END' JabberServer did not echo it. This version, JabberServer echoes the string 'END'. This change was made to make JabberClient1.java simpler.

//: TIEJ:X1:JabberServer.java

// Very simple server that just

// echoes whatever the client sends.

// {RunByHand}

import java.io.*;

import java.net.*;

 

public class JabberServer {

// Choose a port outside of the range 1-1024:

public static final int PORT = 8080;

public static void main(String[] args)

throws IOException {

ServerSocket s = new ServerSocket(PORT);

System.out.println("Started: " + s);

try {

// Blocks until a connection occurs:

Socket socket = s.accept();

try {

System.out.println(

"Connection accepted: "+ socket);

BufferedReader in =

new BufferedReader(

new InputStreamReader(

socket.getInputStream()));

// Output is automatically flushed

// by PrintWriter:

BufferedWriter out =

new BufferedWriter(

new OutputStreamWriter(

socket.getOutputStream()));

while(true) {

String str = in.readLine();

System.out.println("Echoing: " + str);

out.write(str, 0, str.length());

out.newLine();

out.flush();

if(str.equals("END")) break;

}

// Always close the two sockets...

} finally {

System.out.println("closing...");

socket.close();

}

} finally {

s.close();

}

}

} ///:~

 

More to networking

There’s actually a lot more to networking than can be covered in this introductory treatment. Java networking also provides fairly extensive support for URLs, including protocol handlers for different types of content that can be discovered at an Internet site. You can find other Java networking features fully and carefully described in Java Network Programming by Elliotte Rusty Harold (O’Reilly, 1997).

Exercises

7. Compile and run the JabberServer and JabberClient programs in this chapter. Now edit the files to remove all of the buffering for the input and output, then compile and run them again to observe the results.

1. Create a server that asks for a password, then opens a file and sends the file over the network connection. Create a client that connects to this server, gives the appropriate password, then captures and saves the file. Test the pair of programs on your machine using the localhost (the local loopback IP address 127.0.0.1 produced by calling InetAddress.getByName(null)).

1. Modify the server in Exercise 2 so that it uses multithreading to handle multiple clients.

2. Modify JabberClient.java so that output flushing doesn’t occur and observe the effect.

3. Modify MultiJabberServer so that it uses thread pooling. Instead of throwing away a thread each time a client disconnects, the thread should put itself into an “available pool” of threads. When a new client wants to connect, the server will look in the available pool for a thread to handle the request, and if one isn’t available, make a new one. This way the number of threads necessary will naturally grow to the required quantity. The value of thread pooling is that it doesn’t require the overhead of creating and destroying a new thread for each new client.

4. Starting with ShowHTML.java, create an applet that is a password-protected gateway to a particular portion of your Web site.


Remote Method Invocation (RMI)

Traditional approaches to executing code on other machines across a network have been confusing as well as tedious and error-prone to implement. The nicest way to think about this problem is that some object happens to live on another machine, and that you can send a message to the remote object and get a result as if the object lived on your local machine. This simplification is exactly what Java Remote Method Invocation (RMI) allows you to do. This section walks you through the steps necessary to create your own RMI objects.

Remote interfaces

RMI makes heavy use of interfaces. When you want to create a remote object, you mask the underlying implementation by passing around an interface. Thus, when the client gets a reference to a remote object, what they really get is an interface reference, which happens to connect to some local stub code that talks across the network. But you don’t think about this, you just send messages via your interface reference.

When you create a remote interface, you must follow these guidelines:

·         The remote interface must be public (it cannot have “package access,” that is, it cannot be “friendly”). Otherwise, a client will get an error when attempting to load a remote object that implements the remote interface.

·         The remote interface must extend the interface java.rmi.Remote.

·         Each method in the remote interface must declare java.rmi.RemoteException in its throws clause in addition to any application-specific exceptions.

·         A remote object passed as an argument or return value (either directly or embedded within a local object) must be declared as the remote interface, not the implementation class.

Here’s a simple remote interface that represents an accurate time service:

//: c15:rmi:PerfectTimeI.java

// The PerfectTime remote interface.

package c15.rmi;

import java.rmi.*;

 

public interface PerfectTimeI extends Remote {

long getPerfectTime() throws RemoteException;

} ///:~

 

It looks like any other interface except that it extends Remote and all of its methods throw RemoteException. Remember that all the methods of an interface are automatically public.

Implementing the remote interface

The server must contain a class that extends UnicastRemoteObject and implements the remote interface. This class can also have additional methods, but only the methods in the remote interface are available to the client, of course, since the client will get only a reference to the interface, not the class that implements it.

You must explicitly define the constructor for the remote object even if you’re only defining a default constructor that calls the base-class constructor. You must write it out since it must throw RemoteException.

Here’s the implementation of the remote interface PerfectTimeI:

//: c15:rmi:PerfectTime.java

// The implementation of

// the PerfectTime remote object.

// {Broken}

package c15.rmi;

import java.rmi.*;

import java.rmi.server.*;

import java.rmi.registry.*;

import java.net.*;

 

public class PerfectTime

extends UnicastRemoteObject

implements PerfectTimeI {

// Implementation of the interface:

public long getPerfectTime()

throws RemoteException {

return System.currentTimeMillis();

}

// Must implement constructor

// to throw RemoteException:

public PerfectTime() throws RemoteException {

// super(); // Called automatically

}

// Registration for RMI serving. Throw

// exceptions out to the console.

public static void main(String[] args)

throws Exception {

System.setSecurityManager(

new RMISecurityManager());

PerfectTime pt = new PerfectTime();

Naming.bind(

"//peppy:2005/PerfectTime", pt);

System.out.println("Ready to do time");

}

} ///:~

 

Here, main( ) handles all the details of setting up the server. When you’re serving RMI objects, at some point in your program you must:

·       Create and install a security manager that supports RMI. The only one available for RMI as part of the Java distribution is RMISecurityManager.

·       Create one or more instances of a remote object. Here, you can see the creation of the PerfectTime object.

·       Register at least one of the remote objects with the RMI remote object registry for bootstrapping purposes. One remote object can have methods that produce references to other remote objects. This allows you to set it up so the client must go to the registry only once, to get the first remote object.

Setting up the registry

Here, you see a call to the static method Naming.bind( ). However, this call requires that the registry be running as a separate process on the computer. The name of the registry server is rmiregistry, and under 32-bit Windows you say:

start rmiregistry

 

to start it in the background. On Unix, the command is:

rmiregistry &

 

Like many network programs, the rmiregistry is located at the IP address of whatever machine started it up, but it must also be listening at a port. If you invoke the rmiregistry as above, with no argument, the registry’s port will default to 1099. If you want it to be at some other port, you add an argument on the command line to specify the port. For this example, the port is located at 2005, so the rmiregistry should be started like this under 32-bit Windows:

start rmiregistry 2005

 

or for Unix:

rmiregistry 2005 &

 

The information about the port must also be given to the bind( ) command, as well as the IP address of the machine where the registry is located. But this brings up what can be a frustrating problem if you’re expecting to test RMI programs locally the way the network programs have been tested so far in this chapter. In the JDK 1.1.1 release, there are a couple of problems:[2]

1.       localhost does not work with RMI. Thus, to experiment with RMI on a single machine, you must provide the name of the machine. To find out the name of your machine under 32-bit Windows, go to the control panel and select “Network.” Select the “Identification” tab, and you’ll see your computer name. In my case, I called my computer “Peppy.” It appears that capitalization is ignored.

2.      RMI will not work unless your computer has an active TCP/IP connection, even if all your components are just talking to each other on the local machine. This means that you must connect to your Internet service provider before trying to run the program or you’ll get some obscure exception messages.

With all this in mind, the bind( ) command becomes:

Naming.bind("//peppy:2005/PerfectTime", pt);

 

If you are using the default port 1099, you don’t need to specify a port, so you could say:

Naming.bind("//peppy/PerfectTime", pt);

 

You should be able to perform local testing by leaving off the IP address and using only the identifier:

Naming.bind("PerfectTime", pt);

 

The name for the service is arbitrary; it happens to be PerfectTime here, just like the name of the class, but you could call it anything you want. The important thing is that it’s a unique name in the registry that the client knows to look for to procure the remote object. If the name is already in the registry, you’ll get an AlreadyBoundException. To prevent this, you can always use rebind( ) instead of bind( ), since rebind( ) either adds a new entry or replaces the one that’s already there.

Even though main( ) exits, your object has been created and registered so it’s kept alive by the registry, waiting for a client to come along and request it. As long as the rmiregistry is running and you don’t call Naming.unbind( ) on your name, the object will be there. For this reason, when you’re developing your code you need to shut down the rmiregistry and restart it when you compile a new version of your remote object.

You aren’t forced to start up rmiregistry as an external process. If you know that your application is the only one that’s going to use the registry, you can start it up inside your program with the line:

LocateRegistry.createRegistry(2005);

 

Like before, 2005 is the port number we happen to be using in this example. This is the equivalent of running rmiregistry 2005 from a command line, but it can often be more convenient when you’re developing RMI code since it eliminates the extra steps of starting and stopping the registry. Once you’ve executed this code, you can bind( ) using Naming as before.

Creating stubs and skeletons

If you compile and run PerfectTime.java, it won’t work even if you have the rmiregistry running correctly. That’s because the framework for RMI isn’t all there yet. You must first create the stubs and skeletons that provide the network connection operations and allow you to pretend that the remote object is just another local object on your machine.

What’s going on behind the scenes is complex. Any objects that you pass into or return from a remote object must implement Serializable (if you want to pass remote references instead of the entire objects, the object arguments can implement Remote), so you can imagine that the stubs and skeletons are automatically performing serialization and deserialization as they “marshal” all of the arguments across the network and return the result. Fortunately, you don’t have to know any of this, but you do have to create the stubs and skeletons. This is a simple process: you invoke the rmic tool on your compiled code, and it creates the necessary files. So the only requirement is that another step be added to your compilation process.

The rmic tool is particular about packages and classpaths, however. PerfectTime.java is in the package c15.rmi, and even if you invoke rmic in the same directory in which PerfectTime.class is located, rmic won’t find the file, since it searches the classpath. So you must specify the location off the class path, like so:

rmic c15.rmi.PerfectTime

 

You don’t have to be in the directory containing PerfectTime.class when you execute this command, but the results will be placed in the current directory.

When rmic runs successfully, you’ll have two new classes in the directory:

PerfectTime_Stub.class

PerfectTime_Skel.class

 

corresponding to the stub and skeleton. Now you’re ready to get the server and client to talk to each other.

Using the remote object

The whole point of RMI is to make the use of remote objects simple. The only extra thing that you must do in your client program is to look up and fetch the remote interface from the server. From then on, it’s just regular Java programming: sending messages to objects. Here’s the program that uses PerfectTime:

//: c15:rmi:DisplayPerfectTime.java

// Uses remote object PerfectTime.

// {Broken}

package c15.rmi;

import java.rmi.*;

import java.rmi.registry.*;

 

public class DisplayPerfectTime {

public static void main(String[] args)

throws Exception {

System.setSecurityManager(

new RMISecurityManager());

PerfectTimeI t =

(PerfectTimeI)Naming.lookup(

"//peppy:2005/PerfectTime");

for(int i = 0; i < 10; i++)

System.out.println("Perfect time = " +

t.getPerfectTime());

}

} ///:~

 

The ID string is the same as the one used to register the object with Naming, and the first part represents the URL and port number. Since you’re using a URL, you can also specify a machine on the Internet.

What comes back from Naming.lookup( ) must be cast to the remote interface, not to the class. If you use the class instead, you’ll get an exception.

You can see in the method call

t.getPerfectTime()

 

that once you have a reference to the remote object, programming with it is indistinguishable from programming with a local object (with one difference: remote methods throw RemoteException).

Summary

Exercises

 


Connecting to Databases

It has been estimated that half of all software development involves client/server operations. A great promise of Java has been the ability to build platform-independent client/server database applications. This has come to fruition with Java DataBase Connectivity (JDBC).

One of the major problems with databases has been the feature wars between the database companies. There is a “standard” database language, Structured Query Language (SQL-92), but you must usually know which database vendor you’re working with despite the standard. JDBC is designed to be platform-independent, so you don’t need to worry about the database you’re using while you’re programming. However, it’s still possible to make vendor-specific calls from JDBC so you aren’t restricted from doing what you must.

One place where programmers may need to use SQL type names is in the SQL TABLE CREATE statement when they are creating a new database table and defining the SQL type for each column. Unfortunately there are significant variations between SQL types supported by different database products. Different databases that support SQL types with the same semantics and structure may give those types different names. Most major databases support an SQL data type for large binary values: in Oracle this type is called a LONG RAW, Sybase calls it IMAGE, Informix calls it BYTE, and DB2 calls it LONG VARCHAR FOR BIT DATA. Therefore, if database portability is a goal you should try to use only generic SQL type identifiers.

Portability is an issue when writing for a book where readers may be testing the examples with all kinds of unknown data stores. I have tried to write these examples to be as portable as possible. You should also notice that the database-specific code has been isolated in order to centralize any changes that you may need to perform to get the examples operational in your environment.

JDBC, like many of the APIs in Java, is designed for simplicity. The method calls you make correspond to the logical operations you’d think of doing when gathering data from a database: connect to the database, create a statement and execute the query, and look at the result set.

To allow this platform independence, JDBC provides a driver manager that dynamically maintains all the driver objects that your database queries will need. So if you have three different kinds of vendor databases to connect to, you’ll need three different driver objects. The driver objects register themselves with the driver manager at the time of loading, and you can force the loading using Class.forName( ).

To open a database, you must create a “database URL” that specifies:

1. That you’re using JDBC with “jdbc.”

2. The “subprotocol”: the name of the driver or the name of a database connectivity mechanism. Since the design of JDBC was inspired by ODBC, the first subprotocol available is the “jdbc-odbc bridge,” specified by “odbc.”

3. The database identifier. This varies with the database driver used, but it generally provides a logical name that is mapped by the database administration software to a physical directory where the database tables are located. For your database identifier to have any meaning, you must register the name using your database administration software. (The process of registration varies from platform to platform.)

All this information is combined into one string, the “database URL.” For example, to connect through the ODBC subprotocol to a database identified as “people,” the database URL could be:

String dbUrl = "jdbc:odbc:people";

 

If you’re connecting across a network, the database URL will contain the connection information identifying the remote machine and can become a bit intimidating. Here is an example of a CloudScape database being called from a remote client utilizing RMI:

jdbc:rmi://192.168.170.27:1099/jdbc:cloudscape:db

 

This database URL is really two jdbc calls in one. The first part “jdbc:rmi://192.168.170.27:1099/” uses RMI to make the connection to the remote database engine listening on port 1099 at IP Address 192.168.170.27. The second part of the URL, “jdbc:cloudscape:db” conveys the more typical settings using the subprotocol and database name but this will only happen after the first section has made the connection via RMI to the remote machine.

When you’re ready to connect to the database, call the static method DriverManager.getConnection( ) and pass it the database URL, the user name, and a password to get into the database. You get back a Connection object that you can then use to query and manipulate the database.

The following example opens a database of contact information and looks for a person’s last name as given on the command line. It selects only the names of people that have email addresses, then prints out all the ones that match the given last name:

//: c15:jdbc:Lookup.java

// Looks up email addresses in a

// local database using JDBC.

// {Broken}

import java.sql.*;

 

public class Lookup {

public static void main(String[] args)

throws SQLException, ClassNotFoundException {

String dbUrl = "jdbc:odbc:people";

String user = "";

String password = "";

// Load the driver (registers itself)

Class.forName(

"sun.jdbc.odbc.JdbcOdbcDriver");

Connection c = DriverManager.getConnection(

dbUrl, user, password);

Statement s = c.createStatement();

// SQL code:

ResultSet r =

s.executeQuery(

"SELECT FIRST, LAST, EMAIL " +

"FROM people.csv people " +

"WHERE " +

"(LAST='" + args[0] + "') " +

" AND (EMAIL Is Not Null) " +

"ORDER BY FIRST");

while(r.next()) {

// Capitalization doesn't matter:

System.out.println(

r.getString("Last") + ", "

+ r.getString("fIRST")

+ ": " + r.getString("EMAIL") );

}

s.close(); // Also closes ResultSet

}

} ///:~

 

You can see the creation of the database URL as previously described. In this example, there is no password protection on the database so the user name and password are empty strings.

Once the connection is made with DriverManager.getConnection( ), you can use the resulting Connection object to create a Statement object using the createStatement( ) method. With the resulting Statement, you can call executeQuery( ), passing in a string containing an SQL-92 standard SQL statement. (You’ll see shortly how you can generate this statement automatically, so you don’t have to know much about SQL.)

The executeQuery( ) method returns a ResultSet object, which is an iterator: the next( ) method moves the iterator to the next record in the statement, or returns false if the end of the result set has been reached. You’ll always get a ResultSet object back from executeQuery( ) even if a query results in an empty set (that is, an exception is not thrown). Note that you must call next( ) once before trying to read any record data. If the result set is empty, this first call to next( ) will return false. For each record in the result set, you can select the fields using (among other approaches) the field name as a string. Also note that the capitalization of the field name is ignored—it doesn’t matter with an SQL database. You determine the type you’ll get back by calling getInt( ), getString( ), getFloat( ), etc. At this point, you’ve got your database data in Java native format and can do whatever you want with it using ordinary Java code.

Getting the example to work

With JDBC, understanding the code is relatively simple. The confusing part is making it work on your particular system. The reason this is confusing is that it requires you to figure out how to get your JDBC driver to load properly, and how to set up a database using your database administration software.

Of course, this process can vary radically from machine to machine, but the process I used to make it work under 32-bit Windows might give you clues to help you attack your own situation.

Step 1: Find the JDBC Driver

The program above contains the statement:

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

 

This implies a directory structure, which is deceiving. With this particular installation of JDK 1.1, there was no file called JdbcOdbcDriver.class, so if you looked at this example and went searching for it you’d be frustrated. Other published examples use a pseudo name, such as “myDriver.ClassName,” which is less than helpful. In fact, the load statement above for the jdbc-odbc driver (the only one that actually comes with the JDK) appears in only a few places in the online documentation (in particular, a page labeled “JDBC-ODBC Bridge Driver”). If the load statement above doesn’t work, then the name might have been changed as part of a Java version change, so you should hunt through the documentation again.

If the load statement is wrong, you’ll get an exception at this point. To test whether your driver load statement is working correctly, comment out the code after the statement and up to the catch clause; if the program throws no exceptions it means that the driver is loading properly.

Step 2: Configure the database

Again, this is specific to 32-bit Windows; you might need to do some research to figure it out for your own platform.

First, open the control panel. You might find two icons that say “ODBC.” You must use the one that says “32bit ODBC,” since the other one is for backward compatibility with 16-bit ODBC software and will produce no results for JDBC. When you open the “32bit ODBC” icon, you’ll see a tabbed dialog with a number of tabs, including “User DSN,” “System DSN,” “File DSN,” etc., in which “DSN” means “Data Source Name.” It turns out that for the JDBC-ODBC bridge, the only place where it’s important to set up your database is “System DSN,” but you’ll also want to test your configuration and create queries, and for that you’ll also need to set up your database in “File DSN.” This will allow the Microsoft Query tool (that comes with Microsoft Office) to find the database. Note that other query tools are also available from other vendors.

The most interesting database is one that you’re already using. Standard ODBC supports a number of different file formats including such venerable workhorses as DBase. However, it also includes the simple “comma-separated ASCII” format, which virtually every data tool has the ability to write. In my case, I just took my “people” database that I’ve been maintaining for years using various contact-management tools and exported it as a comma-separated ASCII file (these typically have an extension of .csv). In the “System DSN” section I chose “Add,” chose the text driver to handle my comma-separated ASCII file, and then un-checked “use current directory” to allow me to specify the directory where I exported the data file.

You’ll notice when you do this that you don’t actually specify a file, only a directory. That’s because a database is typically represented as a collection of files under a single directory (although it could be represented in other forms as well). Each file usually contains a single table, and the SQL statements can produce results that are culled from multiple tables in the database (this is called a join). A database that contains only a single table (like my “people” database) is usually called a flat-file database. Most problems that go beyond the simple storage and retrieval of data generally require multiple tables that must be related by joins to produce the desired results, and these are called relational databases.

Step 3: Test the configuration

To test the configuration you’ll need a way to discover whether the database is visible from a program that queries it. Of course, you can simply run the JDBC program example above, up to and including the statement:

Connection c = DriverManager.getConnection(

dbUrl, user, password);

 

If an exception is thrown, your configuration was incorrect.

However, it’s useful to get a query-generation tool involved at this point. I used Microsoft Query that came with Microsoft Office, but you might prefer something else. The query tool must know where the database is, and Microsoft Query required that I go to the ODBC Administrator’s “File DSN” tab and add a new entry there, again specifying the text driver and the directory where my database lives. You can name the entry anything you want, but it’s helpful to use the same name you used in “System DSN.”

Once you’ve done this, you will see that your database is available when you create a new query using your query tool.

Step 4: Generate your SQL query

The query that I created using Microsoft Query not only showed me that my database was there and in good order, but it also automatically created the SQL code that I needed to insert into my Java program. I wanted a query that would search for records that had the last name that was typed on the command line when starting the Java program. So as a starting point, I searched for a specific last name, “Eckel.” I also wanted to display only those names that had email addresses associated with them. The steps I took to create this query were:

1.Start a new query and use the Query Wizard. Select the “people” database. (This is the equivalent of opening the database connection using the appropriate database URL.)

2.Select the “people” table within the database. From within the table, choose the columns FIRST, LAST, and EMAIL.

3.Under “Filter Data,” choose LAST and select “equals” with an argument of “Eckel.” Click the “And” radio button.

4.Choose EMAIL and select “Is not Null.”

5.Under “Sort By,” choose FIRST.

The result of this query will show you whether you’re getting what you want.

Now you can press the SQL button and without any research on your part, up will pop the correct SQL code, ready for you to cut and paste. For this query, it looked like this:

SELECT people.FIRST, people.LAST, people.EMAIL

FROM people.csv people

WHERE (people.LAST='Eckel') AND

(people.EMAIL Is Not Null)

ORDER BY people.FIRST

 

Especially with more complicated queries it’s easy to get things wrong, but by using a query tool you can interactively test your queries and automatically generate the correct code. It’s hard to argue the case for doing this by hand.

Step 5: Modify and paste in your query

You’ll notice that the code above looks different from what’s used in the program. That’s because the query tool uses full qualification for all of the names, even when there’s only one table involved. (When more than one table is involved, the qualification prevents collisions between columns from different tables that have the same names.) Since this query involves only one table, you can optionally remove the “people” qualifier from most of the names, like this:

SELECT FIRST, LAST, EMAIL

FROM people.csv people

WHERE (LAST='Eckel') AND

(EMAIL Is Not Null)

ORDER BY FIRST

 

In addition, you don’t want this program to be hard coded to look for only one name. Instead, it should hunt for the name given as the command-line argument. Making these changes and turning the SQL statement into a dynamically-created String produces:

"SELECT FIRST, LAST, EMAIL " +

"FROM people.csv people " +

"WHERE " +

"(LAST='" + args[0] + "') " +

" AND (EMAIL Is Not Null) " +

"ORDER BY FIRST");

 

SQL has another way to insert names into a query called stored procedures, which is used for speed. But for much of your database experimentation and for your first cut, building your own query strings in Java is fine.

You can see from this example that by using the tools currently available—in particular the query-building tool—database programming with SQL and JDBC can be quite straightforward.

A GUI version of the lookup program

It’s more useful to leave the lookup program running all the time and simply switch to it and type in a name whenever you want to look someone up. The following program creates the lookup program as an application/applet, and it also adds name completion so the data will show up without forcing you to type the entire last name:

//: c15:jdbc:VLookup.java

// GUI version of Lookup.java.

// <applet code=VLookup

// width=500 height=200></applet>

// {Broken}

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

import javax.swing.event.*;

import java.sql.*;

import com.bruceeckel.swing.*;

 

public class VLookup extends JApplet {

String dbUrl = "jdbc:odbc:people";

String user = "";

String password = "";

Statement s;

JTextField searchFor = new JTextField(20);

JLabel completion =

new JLabel(" ");

JTextArea results = new JTextArea(40, 20);

public void init() {

searchFor.getDocument().addDocumentListener(

new SearchL());

JPanel p = new JPanel();

p.add(new Label("Last name to search for:"));

p.add(searchFor);

p.add(completion);

Container cp = getContentPane();

cp.add(p, BorderLayout.NORTH);

cp.add(results, BorderLayout.CENTER);

try {

// Load the driver (registers itself)

Class.forName(

"sun.jdbc.odbc.JdbcOdbcDriver");

Connection c = DriverManager.getConnection(

dbUrl, user, password);

s = c.createStatement();

} catch(Exception e) {

results.setText(e.toString());

}

}

class SearchL implements DocumentListener {

public void changedUpdate(DocumentEvent e){}

public void insertUpdate(DocumentEvent e){

textValueChanged();

}

public void removeUpdate(DocumentEvent e){

textValueChanged();

}

}

public void textValueChanged() {

ResultSet r;

if(searchFor.getText().length() == 0) {

completion.setText("");

results.setText("");

return;

}

try {

// Name completion:

r = s.executeQuery(

"SELECT LAST FROM people.csv people " +

"WHERE (LAST Like '" +

searchFor.getText() +

"%') ORDER BY LAST");

if(r.next())

completion.setText(

r.getString("last"));

r = s.executeQuery(

"SELECT FIRST, LAST, EMAIL " +

"FROM people.csv people " +

"WHERE (LAST='" +

completion.getText() +

"') AND (EMAIL Is Not Null) " +

"ORDER BY FIRST");

} catch(Exception e) {

results.setText(

searchFor.getText() + "\n");

results.append(e.toString());

return;

}

results.setText("");

try {

while(r.next()) {

results.append(

r.getString("Last") + ", "

+ r.getString("fIRST") +

": " + r.getString("EMAIL") + "\n");

}

} catch(Exception e) {

results.setText(e.toString());

}

}

public static void main(String[] args) {

Console.run(new VLookup(), 500, 200);

}

} ///:~

 

Much of the database logic is the same, but you can see that a DocumentListener is added to listen to the JTextField (see the javax.swing.JTextField entry in the Java HTML documentation from java.sun.com for details), so that whenever you type a new character it first tries to do a name completion by looking up the last name in the database and using the first one that shows up. (It places it in the completion JLabel, and uses that as the lookup text.) This way, as soon as you’ve typed enough characters for the program to uniquely find the name you’re looking for, you can stop.

Why the JDBC API
seems so complex

When you browse the online documentation for JDBC it can seem daunting. In particular, in the DatabaseMetaData interface—which is just huge, contrary to most of the interfaces you see in Java—there are methods such as dataDefinitionCausesTransactionCommit( ), getMaxColumnNameLength( ), getMaxStatementLength( ), storesMixedCaseQuotedIdentifiers( ), supportsANSI92IntermediateSQL( ), supportsLimitedOuterJoins( ), and so on. What’s this all about?

As mentioned earlier, databases have seemed from their inception to be in a constant state of turmoil, primarily because the demand for database applications, and thus database tools, is so great. Only recently has there been any convergence on the common language of SQL (and there are plenty of other database languages in common use). But even with an SQL “standard” there are so many variations on that theme that JDBC must provide the large DatabaseMetaData interface so that your code can discover the capabilities of the particular “standard” SQL database that it’s currently connected to. In short, you can write simple, transportable SQL, but if you want to optimize speed your coding will multiply tremendously as you investigate the capabilities of a particular vendor’s database.

This, of course, is not Java’s fault. The discrepancies between database products are just something that JDBC tries to help compensate for. But bear in mind that your life will be easier if you can either write generic queries and not worry quite as much about performance, or, if you must tune for performance, know the platform you’re writing for so you don’t need to write all that investigation code.

A more sophisticated example

A more interesting example[3] involves a multitable database that resides on a server. Here, the database is meant to provide a repository for community activities and to allow people to sign up for these events, so it is called the Community Interests Database (CID). This example will only provide an overview of the database and its implementation, and is not intended to be an in-depth tutorial on database development. There are numerous books, seminars, and software packages that will help you in the design and development of a database.

In addition, this example presumes the prior installation of an SQL database on a server (although it could also be run on a local machine), and the interrogation and discovery of an appropriate JDBC driver for that database. Several free SQL databases are available, and some are even automatically installed with various flavors of Linux. You are responsible for making the choice of database and locating the JDBC driver; the example here is based on an SQL database system called “Cloudscape,” which can be freely downloaded for development (not deployment) from http://www.Cloudscape.com. You’ll need to follow the instructions in the download in order to properly install and set up Cloudscape.

To keep changes in the connection information simple, the database driver, database URL, user name, and password are placed in a separate class:

//: c15:jdbc:CIDConnect.java

// Database connection information for

// the community interests database (CID).

 

public class CIDConnect {

// All the information specific to CloudScape:

public static String dbDriver =

"COM.cloudscape.core.JDBCDriver";

public static String dbURL =

"jdbc:cloudscape:d:/docs/_work/JSapienDB";

public static String user = "";

public static String password = "";

} ///:~

 

In this example, there is no password protection on the database so the user name and password are empty strings. With Cloudscape, the dbURL contains the directory path where the database is located, but other JDBC drivers will use other ways to encode this information. This example assumes that the database “JSapienDB” has already been created, but in order to get the example to work you’ll need to use the cview tool that comes with Cloudscape in order to create the new database, and then you must change the above dbURL to reflect the path of the database you created.

The database consists of a set of tables that have a structure as shown here:

“Members” contains community member information, “Events” and “Locations” contain information about the activities and where they take place, and “Evtmems” connects events and members that would like to attend that event. You can see that a data member in one table produces a key in another table.

The following class contains the SQL strings that will create these database tables (refer to an SQL guide for an explanation of the SQL code):

//: c15:jdbc:CIDSQL.java

// SQL strings to create the tables for the CID.

 

public class CIDSQL {

public static String[] sql = {

// Create the MEMBERS table:

"drop table MEMBERS",

"create table MEMBERS " +

"(MEM_ID INTEGER primary key, " +

"MEM_UNAME VARCHAR(12) not null unique, "+

"MEM_LNAME VARCHAR(40), " +

"MEM_FNAME VARCHAR(20), " +

"ADDRESS VARCHAR(40), " +

"CITY VARCHAR(20), " +

"STATE CHAR(4), " +

"ZIP CHAR(5), " +

"PHONE CHAR(12), " +

"EMAIL VARCHAR(30))",

"create unique index " +

"LNAME_IDX on MEMBERS(MEM_LNAME)",

// Create the EVENTS table

"drop table EVENTS",

"create table EVENTS " +

"(EVT_ID INTEGER primary key, " +

"EVT_TITLE VARCHAR(30) not null, " +

"EVT_TYPE VARCHAR(20), " +

"LOC_ID INTEGER, " +

"PRICE DECIMAL, " +

"DATETIME TIMESTAMP)",

"create unique index " +

"TITLE_IDX on EVENTS(EVT_TITLE)",

// Create the EVTMEMS table

"drop table EVTMEMS",

"create table EVTMEMS " +

"(MEM_ID INTEGER not null, " +

"EVT_ID INTEGER not null, " +

"MEM_ORD INTEGER)",

"create unique index " +

"EVTMEM_IDX on EVTMEMS(MEM_ID, EVT_ID)",

// Create the LOCATIONS table

"drop table LOCATIONS",

"create table LOCATIONS " +

"(LOC_ID INTEGER primary key, " +

"LOC_NAME VARCHAR(30) not null, " +

"CONTACT VARCHAR(50), " +

"ADDRESS VARCHAR(40), " +

"CITY VARCHAR(20), " +

"STATE VARCHAR(4), " +

"ZIP VARCHAR(5), " +

"PHONE CHAR(12), " +

"DIRECTIONS VARCHAR(4096))",

"create unique index " +

"NAME_IDX on LOCATIONS(LOC_NAME)",

};

} ///:~

 

The following program uses the CIDConnect and CIDSQL information to load the JDBC driver, make a connection to the database, and then create the table structure diagrammed above. To connect with the database, you call the static method DriverManager.getConnection( ), passing it the database URL, the user name, and a password to get into the database. You get back a Connection object that you can use to query and manipulate the database. Once the connection is made you can simply push the SQL to the database, in this case by marching through the CIDSQL array. However, the first time this program is run, the “drop table” command will fail, causing an exception, which is caught, reported, and then ignored. The reason for the “drop table” command is to allow easy experimentation: you can modify the SQL that defines the tables and then rerun the program, causing the old tables to be replaced by the new.

In this example, it makes sense to let the exceptions be thrown out to the console:

//: c15:jdbc:CIDCreateTables.java

// Creates database tables for the

// community interests database.

// {Broken}

import java.sql.*;

 

public class CIDCreateTables {

public static void main(String[] args)

throws SQLException, ClassNotFoundException,

IllegalAccessException {

// Load the driver (registers itself)

Class.forName(CIDConnect.dbDriver);

Connection c = DriverManager.getConnection(

CIDConnect.dbURL, CIDConnect.user,

CIDConnect.password);

Statement s = c.createStatement();

for(int i = 0; i < CIDSQL.sql.length; i++) {

System.out.println(CIDSQL.sql[i]);

try {

s.executeUpdate(CIDSQL.sql[i]);

} catch(SQLException sqlEx) {

System.err.println(

"Probably a 'drop table' failed");

}

}

s.close();

c.close();

}

} ///:~

 

Note that all changes in the database can be controlled by changing Strings in the CIDSQL table, without modifying CIDCreateTables.

executeUpdate( ) will usually return the number of rows that were affected by the SQL statement. executeUpdate( ) is more commonly used to execute INSERT, UPDATE, or DELETE statements that modify one or more rows. For statements such as CREATE TABLE, DROP TABLE, and CREATE INDEX, executeUpdate( ) always returns zero.

To test the database, it is loaded with some sample data. This requires a series of INSERTs followed by a SELECT to produce result set. To make additions and changes to the test data easy, the test data is set up as a two-dimensional array of Objects, and the executeInsert( ) method can then use the information in one row of the table to create the appropriate SQL command.

//: c15:jdbc:LoadDB.java

// Loads and tests the database.

// {Broken}

import java.sql.*;

 

class TestSet {

Object[][] data = {

{ "MEMBERS", new Integer(1),

"dbartlett", "Bartlett", "David",

"123 Mockingbird Lane",

"Gettysburg", "PA", "19312",

"123.456.7890", "bart@you.net" },

{ "MEMBERS", new Integer(2),

"beckel", "Eckel", "Bruce",

"123 Over Rainbow Lane",

"Crested Butte", "CO", "81224",

"123.456.7890", "beckel@you.net" },

{ "MEMBERS", new Integer(3),

"rcastaneda", "Castaneda", "Robert",

"123 Downunder Lane",

"Sydney", "NSW", "12345",

"123.456.7890", "rcastaneda@you.net" },

{ "LOCATIONS", new Integer(1),

"Center for Arts",

"Betty Wright", "123 Elk Ave.",

"Crested Butte", "CO", "81224",

"123.456.7890",

"Go this way then that." },

{ "LOCATIONS", new Integer(2),

"Witts End Conference Center",

"John Wittig", "123 Music Drive",

"Zoneville", "PA", "19123",

"123.456.7890",

"Go that way then this." },

{ "EVENTS", new Integer(1),

"Project Management Myths",

"Software Development",

new Integer(1), new Float(2.50),

"2000-07-17 19:30:00" },

{ "EVENTS", new Integer(2),

"Life of the Crested Dog",

"Archeology",

new Integer(2), new Float(0.00),

"2000-07-19 19:00:00" },

// Match some people with events

{ "EVTMEMS",

new Integer(1), // Dave is going to

new Integer(1), // the Software event.

new Integer(0) },

{ "EVTMEMS",

new Integer(2), // Bruce is going to

new Integer(2), // the Archeology event.

new Integer(0) },

{ "EVTMEMS",

new Integer(3), // Robert is going to

new Integer(1), // the Software event.

new Integer(1) },

{ "EVTMEMS",

new Integer(3), // ... and

new Integer(2), // the Archeology event.

new Integer(1) },

};

// Use the default data set:

public TestSet() {}

// Use a different data set:

public TestSet(Object[][] dat) { data = dat; }

}

 

public class LoadDB {

Statement statement;

Connection connection;

TestSet tset;

public LoadDB(TestSet t) throws SQLException {

tset = t;

try {

// Load the driver (registers itself)

Class.forName(CIDConnect.dbDriver);

} catch(java.lang.ClassNotFoundException e) {

e.printStackTrace(System.err);

}

connection = DriverManager.getConnection(

CIDConnect.dbURL, CIDConnect.user,

CIDConnect.password);

statement = connection.createStatement();

}

public void dispose() throws SQLException {

statement.close();

connection.close();

}

public void executeInsert(Object[] data) {

String sql = "insert into "

+ data[0] + " values(";

for(int i = 1; i < data.length; i++) {

if(data[i] instanceof String)

sql += "'" + data[i] + "'";

else

sql += data[i];

if(i < data.length - 1)

sql += ", ";

}

sql += ')';

System.out.println(sql);

try {

statement.executeUpdate(sql);

} catch(SQLException sqlEx) {

System.err.println("Insert failed.");

while (sqlEx != null) {

System.err.println(sqlEx.toString());

sqlEx = sqlEx.getNextException();

}

}

}

public void load() {

for(int i = 0; i< tset.data.length; i++)

executeInsert(tset.data[i]);

}

// Throw exceptions out to console:

public static void main(String[] args)

throws SQLException {

LoadDB db = new LoadDB(new TestSet());

db.load();

try {

// Get a ResultSet from the loaded database:

ResultSet rs = db.statement.executeQuery(

"select " +

"e.EVT_TITLE, m.MEM_LNAME, m.MEM_FNAME "+

"from EVENTS e, MEMBERS m, EVTMEMS em " +

"where em.EVT_ID = 2 " +

"and e.EVT_ID = em.EVT_ID " +

"and m.MEM_ID = em.MEM_ID");

while (rs.next())

System.out.println(

rs.getString(1) + " " +

rs.getString(2) + ", " +

rs.getString(3));

} finally {

db.dispose();

}

}

} ///:~

 

The TestSet class contains a default set of data that is produced if you use the default constructor; however, you can also create a TestSet object using an alternate data set with the second constructor. The set of data is held in a two-dimensional array of Object because it can be any type, including String or numerical types. The executeInsert( ) method uses RTTI to distinguish between String data (which must be quoted) and non-String data as it builds the SQL command from the data. After printing this command to the console, executeUpdate( ) is used to send it to the database.

The constructor for LoadDB makes the connection, and load( ) steps through the data and calls executeInsert( ) for each record. dispose( ) closes the statement and the connection; to guarantee that this is called, it is placed inside a finally clause.

Once the database is loaded, an executeQuery( ) statement produces a sample result set. Since the query combines several tables, it is an example of a join.

There is more JDBC information available in the electronic documents that come as part of the Java distribution from Sun. In addition, you can find more in the book JDBC Database Access with Java (Hamilton, Cattel, and Fisher, Addison-Wesley, 1997). Other JDBC books appear regularly.

Summary

Exercises

5.Modify CIDCreateTables.java so that it reads the SQL strings from a text file instead of CIDSQL.

6.Configure your system so that you can successfully execute CIDCreateTables.java and LoadDB.java.

7. (More challenging) Take the VLookup.java program and modify it so that when you click on the resulting name it automatically takes that name and copies it to the clipboard (so you can simply paste it into your email). You’ll need to look back at Chapter 13 to remember how to use the clipboard in JFC.


Servlets

Client access from the Internet or corporate intranets is a sure way to allow many users to access data and resources easily[4].

This type of access is based on clients using the World Wide Web standards of Hypertext Markup Language (HTML) and Hypertext Transfer Protocol (HTTP). The Servlet API set abstracts a common solution framework for responding to HTTP requests.

Traditionally, the way to handle a problem such as allowing an Internet client to update a database is to create an HTML page with text fields and a “submit” button. The user types the appropriate information into the text fields and presses the “submit” button. The data is submitted along with a URL that tells the server what to do with the data by specifying the location of a Common Gateway Interface (CGI) program that the server runs, providing the program with the data as it is invoked. The CGI program is typically written in Perl, Python, C, C++, or any language that can read from standard input and write to standard output. That’s all that is provided by the Web server: the CGI program is invoked, and standard streams (or, optionally for input, an environment variable) are used for input and output. The CGI program is responsible for everything else. First it looks at the data and decides whether the format is correct. If not, the CGI program must produce HTML to describe the problem; this page is handed to the Web server (via standard output from the CGI program), which sends it back to the user. The user must usually back up a page and try again. If the data is correct, the CGI program processes the data in an appropriate way, perhaps adding it to a database. It must then produce an appropriate HTML page for the Web server to return to the user.

It would be ideal to go to a completely Java-based solution to this problem—an applet on the client side to validate and send the data, and a servlet on the server side to receive and process the data. Unfortunately, although applets are a proven technology with plenty of support, they have been problematic to use on the Web because you cannot rely on a particular version of Java being available on a client’s Web browser; in fact, you can’t rely on a Web browser supporting Java at all! In an intranet, you can require that certain support be available, which allows a lot more flexibility in what you can do, but on the Web the safest approach is to handle all the processing on the server side and deliver plain HTML to the client. That way, no client will be denied the use of your site because they do not have the proper software installed.

Because servlets provide an excellent solution for server-side programming support, they are one of the most popular reasons for moving to Java. Not only do they provide a framework that replaces CGI programming (and eliminates a number of thorny CGI problems), but all your code has the platform portability gained from using Java, and you have access to all the Java APIs (except, of course, the ones that produce GUIs, like Swing).

The basic servlet

The architecture of the servlet API is that of a classic service provider with a service( ) method through which all client requests will be sent by the servlet container software, and life cycle methods init( ) and destroy( ), which are called only when the servlet is loaded and unloaded (this happens rarely).

public interface Servlet {

public void init(ServletConfig config)

throws ServletException;

public ServletConfig getServletConfig();

public void service(ServletRequest req,

ServletResponse res)

throws ServletException, IOException;

public String getServletInfo();

public void destroy();

}

 

getServletConfig( )’s sole purpose is to return a ServletConfig object that contains initialization and startup parameters for this servlet. getServletInfo( ) returns a string containing information about the servlet, such as author, version, and copyright.

The GenericServlet class is a shell implementation of this interface and is typically not used. The HttpServlet class is an extension of GenericServlet and is designed specifically to handle the HTTP protocol— HttpServlet is the one that you’ll use most of the time.

The most convenient attribute of the servlet API is the auxiliary objects that come along with the HttpServlet class to support it. If you look at the service( ) method in the Servlet interface, you’ll see it has two parameters: ServletRequest and ServletResponse. With the HttpServlet class these two object are extended for HTTP: HttpServletRequest and HttpServletResponse. Here’s a simple example that shows the use of HttpServletResponse:

//: c15:servlets:ServletsRule.java

// {Depends: j2ee.jar}

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

 

public class ServletsRule extends HttpServlet {

int i = 0; // Servlet "persistence"

public void service(HttpServletRequest req,

HttpServletResponse res) throws IOException {

res.setContentType("text/html");

PrintWriter out = res.getWriter();

out.print("<HEAD><TITLE>");

out.print("A server-side strategy");

out.print("</TITLE></HEAD><BODY>");

out.print("<h1>Servlets Rule! " + i++);

out.print("</h1></BODY>");

out.close();

}

} ///:~

 

ServletsRule is about as simple as a servlet can get. The servlet is initialized only once by calling its init( ) method, on loading the servlet after the servlet container is first booted up. When a client makes a request to a URL that happens to represent a servlet, the servlet container intercepts this request and makes a call to the service( ) method, after setting up the HttpServletRequest and HttpServletResponse objects.

The main responsibility of the service( ) method is to interact with the HTTP request that the client has sent, and to build an HTTP response based on the attributes contained within the request. ServletsRule only manipulates the response object without looking at what the client may have sent.

After setting the content type of the response (which must always be done before the Writer or OutputStream is procured), the getWriter( ) method of the response object produces a PrintWriter object, which is used for writing character-based response data (alternatively, getOutputStream( ) produces an OutputStream, used for binary response, which is only utilized in more specialized solutions).

The rest of the program simply sends HTML back to the client (it’s assumed you understand HTML, so that part is not explained) as a sequence of Strings. However, notice the inclusion of the “hit counter” represented by the variable i. This is automatically converted to a String in the print( ) statement.

When you run the program, you’ll notice that the value of i is retained between requests to the servlet. This is an essential property of servlets: since only one servlet of a particular class is loaded into the container, and it is never unloaded (unless the servlet container is terminated, which is something that only normally happens if you reboot the server computer), any fields of that servlet class effectively become persistent objects! This means that you can effortlessly maintain values between servlet requests, whereas with CGI you had to write values to disk in order to preserve them, which required a fair amount of fooling around to get it right, and resulted in a non-cross-platform solution.

Of course, sometimes the Web server, and thus the servlet container, must be rebooted as part of maintenance or during a power failure. To avoid losing any persistent information, the servlet’s init( ) and destroy( ) methods are automatically called whenever the servlet is loaded or unloaded, giving you the opportunity to save data during shutdown, and restore it after rebooting. The servlet container calls the destroy( ) method as it is terminating itself, so you always get an opportunity to save valuable data as long as the server machine is configured in an intelligent way.

There’s one other issue when using HttpServlet. This class provides doGet( ) and doPost( ) methods that differentiate between a CGI “GET” submission from the client, and a CGI “POST.” GET and POST vary only in the details of the way that they submit the data, which is something that I personally would prefer to ignore. However, most published information that I’ve seen seems to favor the creation of separate doGet( ) and doPost( ) methods instead of a single generic service( ) method, which handles both cases. This favoritism seems quite common, but I’ve never seen it explained in a fashion that leads me to believe that it’s anything more than inertia from CGI programmers who are used to paying attention to whether a GET or POST is being used. So in the spirit of “doing the simplest thing that could possibly work,”[5] I will just use the service( ) method in these examples, and let it care about GETs vs. POSTs. However, keep in mind that I might have missed something and so there may in fact be a good reason to use doGet( ) and doPost( ) instead.

Whenever a form is submitted to a servlet, the HttpServletRequest comes preloaded with all the form data, stored as key-value pairs. If you know the names of the fields, you can just use them directly with the getParameter( ) method to look up the values. You can also get an Enumeration (the old form of the Iterator) to the field names, as is shown in the following example. This example also demonstrates how a single servlet can be used to produce the page that contains the form, and to respond to the page (a better solution will be seen later, with JSPs). If the Enumeration is empty, there are no fields; this means no form was submitted. In this case, the form is produced, and the submit button will re-call the same servlet. If fields do exist, however, they are displayed.

//: c15:servlets:EchoForm.java

// Dumps the name-value pairs of any HTML form

// {Depends: j2ee.jar}

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.util.*;

 

public class EchoForm extends HttpServlet {

public void service(HttpServletRequest req,

HttpServletResponse res) throws IOException {

res.setContentType("text/html");

PrintWriter out = res.getWriter();

Enumeration flds = req.getParameterNames();

if(!flds.hasMoreElements()) {

// No form submitted -- create one:

out.print("<html>");

out.print("<form method=\"POST\"" +

" action=\"EchoForm\">");

for(int i = 0; i < 10; i++)

out.print("<b>Field" + i + "</b> " +

"<input type=\"text\""+

" size=\"20\" name=\"Field" + i +

"\" value=\"Value" + i + "\"><br>");

out.print("<INPUT TYPE=submit name=submit"+

" Value=\"Submit\"></form></html>");

} else {

out.print("<h1>Your form contained:</h1>");

while(flds.hasMoreElements()) {

String field= (String)flds.nextElement();

String value= req.getParameter(field);

out.print(field + " = " + value+ "<br>");

}

}

out.close();

}

} ///:~

 

One drawback you’ll notice here is that Java does not seem to be designed with string processing in mind—the formatting of the return page is painful because of line breaks, escaping quote marks, and the “+” signs necessary to build String objects. With a larger HTML page it becomes unreasonable to code it directly into Java. One solution is to keep the page as a separate text file, then open it and hand it to the Web server. If you have to perform any kind of substitution to the contents of the page, it’s not much better since Java has treated string processing so poorly. In these cases you’re probably better off using a more appropriate solution (Python would be my choice; there’s a version that embeds itself in Java called JPython) to generate the response page.

Servlets and multithreading

The servlet container has a pool of threads that it will dispatch to handle client requests. It is quite likely that two clients arriving at the same time could be processing through your service( ) at the same time. Therefore the service( ) method must written in a thread-safe manner. Any access to common resources (files, databases) will need to be guarded by using the synchronized keyword.

The following simple example puts a synchronized clause around the thread’s sleep( ) method. This will block all other threads until the allotted time (five seconds) is all used up. When testing this you should start several browser instances and hit this servlet as quickly as possible in each one—you’ll see that each one has to wait until its turn comes up.

//: c15:servlets:ThreadServlet.java

// {Depends: j2ee.jar}

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

 

public class ThreadServlet extends HttpServlet {

int i;

public void service(HttpServletRequest req,

HttpServletResponse res) throws IOException {

res.setContentType("text/html");

PrintWriter out = res.getWriter();

synchronized(this) {

try {

Thread.currentThread().sleep(5000);

} catch(InterruptedException e) {

System.err.println("Interrupted");

}

}

out.print("<h1>Finished " + i++ + "</h1>");

out.close();

}

} ///:~

 

It is also possible to synchronize the entire servlet by putting the synchronized keyword in front of the service( ) method. In fact, the only reason to use the synchronized clause instead is if the critical section is in an execution path that might not get executed. In that case, you might as well avoid the overhead of synchronizing every time by using a synchronized clause. Otherwise, all the threads will have to wait anyway so you might as well synchronize the whole method.

Handling sessions with servlets

HTTP is a “sessionless” protocol, so you cannot tell from one server hit to another if you’ve got the same person repeatedly querying your site, or if it is a completely different person. A great deal of effort has gone into mechanisms that will allow Web developers to track sessions. Companies could not do e-commerce without keeping track of a client and the items they have put into their shopping cart, for example.

There are several methods of session tracking, but the most common method is with persistent “cookies,” which are an integral part of the Internet standards. The HTTP Working Group of the Internet Engineering Task Force has written cookies into the official standard in RFC 2109 (ds.internic.net/rfc/rfc2109.txt or check www.cookiecentral.com).

A cookie is nothing more than a small piece of information sent by a Web server to a browser. The browser stores the cookie on the local disk, and whenever another call is made to the URL that the cookie is associated with, the cookie is quietly sent along with the call, thus providing the desired information back to that server (generally, providing some way that the server can be told that it’s you calling). Clients can, however, turn off the browser’s ability to accept cookies. If your site must track a client who has turned off cookies, then another method of session tracking (URL rewriting or hidden form fields) must be incorporated by hand, since the session tracking capabilities built into the servlet API are designed around cookies.

The Cookie class

The servlet API (version 2.0 and up) provides the Cookie class. This class incorporates all the HTTP header details and allows the setting of various cookie attributes. Using the cookie is simply a matter of adding it to the response object. The constructor takes a cookie name as the first argument and a value as the second. Cookies are added to the response object before you send any content.

Cookie oreo = new Cookie("TIJava", "2002");

res.addCookie(oreo);

 

Cookies are recovered by calling the getCookies( ) method of the HttpServletRequest object, which returns an array of cookie objects.

Cookie[] cookies = req.getCookies();

 

You can then call getValue( ) for each cookie, to produce a String containing the cookie contents. In the above example, getValue("TIJava") will produce a String containing “2002.”

The Session class

A session is one or more page requests by a client to a Web site during a defined period of time. If you buy groceries online, for example, you want a session to be confined to the period from when you first add an item to “my shopping cart” to the point where you check out. Each item you add to the shopping cart will result in a new HTTP connection, which has no knowledge of previous connections or items in the shopping cart. To compensate for this lack of information, the mechanics supplied by the cookie specification allow your servlet to perform session tracking.

A servlet Session object lives on the server side of the communication channel; its goal is to capture useful data about this client as the client moves through and interacts with your Web site. This data may be pertinent for the present session, such as items in the shopping cart, or it may be data such as authentication information that was entered when the client first entered your Web site, and which should not have to be reentered during a particular set of transactions.

The Session class of the servlet API uses the Cookie class to do its work. However, all the Session object needs is some kind of unique identifier stored on the client and passed to the server. Web sites may also use the other types of session tracking but these mechanisms will be more difficult to implement as they are not encapsulated into the servlet API (that is, you must write them by hand to deal with the situation when the client has disabled cookies).

Here’s an example that implements session tracking with the servlet API:

//: c15:servlets:SessionPeek.java

// Using the HttpSession class.

// {Depends: j2ee.jar}

import java.io.*;

import java.util.*;

import javax.servlet.*;

import javax.servlet.http.*;

 

public class SessionPeek extends HttpServlet {

public void service(HttpServletRequest req,

HttpServletResponse res)

throws ServletException, IOException {

// Retrieve Session Object before any

// output is sent to the client.

HttpSession session = req.getSession();

res.setContentType("text/html");

PrintWriter out = res.getWriter();

out.println("<HEAD><TITLE> SessionPeek ");

out.println(" </TITLE></HEAD><BODY>");

out.println("<h1> SessionPeek </h1>");

// A simple hit counter for this session.

Integer ival = (Integer)

session.getAttribute("sesspeek.cntr");

if(ival==null)

ival = new Integer(1);

else

ival = new Integer(ival.intValue() + 1);

session.setAttribute("sesspeek.cntr", ival);

out.println("You have hit this page <b>"

+ ival + "</b> times.<p>");

out.println("<h2>");

out.println("Saved Session Data </h2>");

// Loop through all data in the session:

Enumeration sesNames =

session.getAttributeNames();

while(sesNames.hasMoreElements()) {

String name =

sesNames.nextElement().toString();

Object value = session.getAttribute(name);

out.println(name + " = " + value + "<br>");

}

out.println("<h3> Session Statistics </h3>");

out.println("Session ID: "

+ session.getId() + "<br>");

out.println("New Session: " + session.isNew()

+ "<br>");

out.println("Creation Time: "

+ session.getCreationTime());

out.println("<I>(" +

new Date(session.getCreationTime())

+ ")</I><br>");

out.println("Last Accessed Time: " +

session.getLastAccessedTime());

out.println("<I>(" +

new Date(session.getLastAccessedTime())

+ ")</I><br>");

out.println("Session Inactive Interval: "

+ session.getMaxInactiveInterval());

out.println("Session ID in Request: "

+ req.getRequestedSessionId() + "<br>");

out.println("Is session id from Cookie: "

+ req.isRequestedSessionIdFromCookie()

+ "<br>");

out.println("Is session id from URL: "

+ req.isRequestedSessionIdFromURL()

+ "<br>");

out.println("Is session id valid: "

+ req.isRequestedSessionIdValid()

+ "<br>");

out.println("</BODY>");

out.close();

}

public String getServletInfo() {

return "A session tracking servlet";

}

} ///:~

 

Inside the service( ) method, getSession( ) is called for the request object, which returns the Session object associated with this request. The Session object does not travel across the network, but instead it lives on the server and is associated with a client and its requests.

getSession( ) comes in two versions: no parameter, as used here, and getSession(boolean). getSession(true) is equivalent to getSession( ). The only reason for the boolean is to state whether you want the session object created if it is not found. getSession(true) is the most likely call, hence getSession( ).

The Session object, if it is not new, will give us details about the client from previous visits. If the Session object is new then the program will start to gather information about this client’s activities on this visit. Capturing this client information is done through the setAttribute( ) and getAttribute( ) methods of the session object.

java.lang.Object getAttribute(java.lang.String)

void setAttribute(java.lang.String name,

java.lang.Object value)

 

The Session object uses a simple name-value pairing for loading information. The name is a String, and the value can be any object derived from java.lang.Object. SessionPeek keeps track of how many times the client has been back during this session. This is done with an Integer object named sesspeek.cntr. If the name is not found an Integer is created with value of one, otherwise an Integer is created with the incremented value of the previously held Integer. The new Integer is placed into the Session object. If you use same key in a setAttribute( ) call, then the new object overwrites the old one. The incremented counter is used to display the number of times that the client has visited during this session.

getAttributeNames( ) is related to getAttribute( ) and setAttribute( ); it returns an enumeration of the names of the objects that are bound to the Session object. A while loop in SessionPeek shows this method in action.

You may wonder how long a Session object hangs around. The answer depends on the servlet container you are using; they usually default to 30 minutes (1800 seconds), which is what you should see from the ServletPeek call to getMaxInactiveInterval( ). Tests seem to produce mixed results between servlet containers. Sometimes the Session object can hang around overnight, but I have never seen a case where the Session object disappears in less than the time specified by the inactive interval. You can try this by setting the inactive interval with setMaxInactiveInterval( ) to 5 seconds and see if your Session object hangs around or if it is cleaned up at the appropriate time. This may be an attribute you will want to investigate while choosing a servlet container.

Running the servlet examples

If you are not already working with an application server that handles Sun’s servlet and JSP technologies for you, you may download the Tomcat implementation of Java servlets and JSPs, which is a free, open-source implementation of servlets, and is the official reference implementation sanctioned by Sun. It can be found at jakarta.apache.org.

Follow the instructions for installing the Tomcat implementation, then edit the server.xml file to point to the location in your directory tree where your servlets will be placed. Once you start up the Tomcat program you can test your servlet programs.

Summary

This has only been a brief introduction to servlets; there are entire books on the subject. However, this introduction should give you enough ideas to get you started. In addition, many of the ideas in the next section are backward compatible with servlets.

Exercises

8.Modify ServletsRule.java by overriding the destroy( ) method to save the value of i to a file, and and the init( ) method to restore the value. Demonstrate that it works by rebooting the servlet container. If you do not have an existing servlet container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run servlets.

9.Create a servlet that adds a cookie to the response object, thereby storing it on the client’s site. Add code to the servlet that retrieves and displays the cookie. If you do not have an existing servlet container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run servlets.

10.Create a servlet that uses a Session object to store session information of your choosing. In the same servlet, retrieve and display that session information. If you do not have an existing servlet container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run servlets.

11.Create a servlet that changes the inactive interval of a session to 5 seconds by calling getMaxInactiveInterval( ). Test to see that the session does indeed expire after 5 seconds. If you do not have an existing servlet container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run servlets.


JavaServer Pages

JavaServer Pages (JSP) is a standard Java extension that is defined on top of the servlet Extensions. The goal of JSPs is the simplified creation and management of dynamic Web pages.

The previously mentioned, freely available Tomcat reference implementation from jakarta.apache.org automatically supports JSPs.

JSPs allow you to combine the HTML of a Web page with pieces of Java code in the same document. The Java code is surrounded by special tags that tell the JSP container that it should use the code to generate a servlet, or part of one. The benefit of JSPs is that you can maintain a single document that represents both the page and the Java code that enables it. The downside is that the maintainer of the JSP page must be skilled in both HTML and Java (however, GUI builder environments for JSPs should be forthcoming).

The first time a JSP is loaded by the JSP container (which is typically associated with, or even part of, a Web server), the servlet code necessary to fulfill the JSP tags is automatically generated, compiled, and loaded into the servlet container. The static portions of the HTML page are produced by sending static String objects to write( ). The dynamic portions are included directly into the servlet.

From then on, as long as the JSP source for the page is not modified, it behaves as if it were a static HTML page with associated servlets (all the HTML code is actually generated by the servlet, however). If you modify the source code for the JSP, it is automatically recompiled and reloaded the next time that page is requested. Of course, because of all this dynamism you’ll see a slow response for the first-time access to a JSP. However, since a JSP is usually used much more than it is changed, you will normally not be affected by this delay.

The structure of a JSP page is a cross between a servlet and an HTML page. The JSP tags begin and end with angle brackets, just like HTML tags, but the tags also include percent signs, so all JSP tags are denoted by

<% JSP code here %>

 

The leading percent sign may be followed by other characters that determine the precise type of JSP code in the tag.

Here’s an extremely simple JSP example that uses a standard Java library call to get the current time in milliseconds, which is then divided by 1000 to produce the time in seconds. Since a JSP expression (the <%= ) is used, the result of the calculation is coerced into a String and placed on the generated Web page:

//:! c15:jsp:ShowSeconds.jsp

<html><body>

<H1>The time in seconds is:

<%= System.currentTimeMillis()/1000 %></H1>

</body></html>

///:~

 

In the JSP examples in this book, the exclamation point in the first “comment tag line” means that the first and last lines will not be included in the actual code file that is extracted and placed in the book’s source-code tree.

When the client creates a request for the JSP page, the Web server must have been configured to relay the request to the JSP container, which then invokes the page. As mentioned above, the first time the page is invoked, the components specified by the page are generated and compiled by the JSP container as one or more servlets. In the above example, the servlet will contain code to configure the HttpServletResponse object, produce a PrintWriter object (which is always named out), and then turn the time calculation into a String which is sent to out. As you can see, all this is accomplished with a very succinct statement, but the average HTML programmer/Web designer will not have the skills to write such code.

Implicit objects

Servlets include classes that provide convenient utilities, such as HttpServletRequest, HttpServletResponse, Session, etc. Objects of these classes are built into the JSP specification and automatically available for use in your JSP without writing any extra lines of code. The implicit objects in a JSP are detailed in the table below.

Implicit variable

Of Type (javax.servlet)

Description

Scope

request

protocol dependent subtype of HttpServletRequest

The request that triggers the service invocation.

request

response

protocol dependent subtype of HttpServletResponse

The response to the request.

page

pageContext

jsp.PageContext

The page context encapsulates implementation-dependent features and provides convenience methods and namespace access for this JSP.

page

session

Protocol dependent subtype of http.HttpSession

The session object created for the requesting client. See servlet Session object.

session

application

ServletContext

The servlet context obtained from the servlet configuration object (e.g., getServletConfig(), getContext( ).

app

out

jsp.JspWriter

The object that writes into the output stream.

page

config

ServletConfig

The ServletConfig for this JSP.

page

page

java.lang.Object

The instance of this page’s implementation class processing the current request.

page

The scope of each object can vary significantly. For example, the session object has a scope which exceeds that of a page, as it many span several client requests and pages. The application object can provide services to a group of JSP pages that together represent a Web application.

JSP directives

Directives are messages to the JSP container and are denoted by the “@”:

<%@ directive {attr="value"}* %>

 

Directives do not send anything to the out stream, but they are important in setting up your JSP page’s attributes and dependencies with the JSP container. For example, the line:

<%@ page language="java" %>

 

says that the scripting language being used within the JSP page is Java. In fact, the JSP specification only describes the semantics of scripts for the language attribute equal to “Java.” The intent of this directive is to build flexibility into the JSP technology. In the future, if you were to choose another language, say Python (a good scripting choice), then that language would have to support the Java Run-time Environment by exposing the Java technology object model to the scripting environment, especially the implicit variables defined above, JavaBeans properties, and public methods.

The most important directive is the page directive. It defines a number of page dependent attributes and communicates these attributes to the JSP container. These attributes include: language, extends, import, session, buffer, autoFlush, isThreadSafe, info and errorPage. For example:

<%@ page session=”true” import=”java.util.*” %>

 

This line first indicates that the page requires participation in an HTTP session. Since we have not set the language directive the JSP container defaults to using Java and the implicit script language variable named session is of type javax.servlet.http.HttpSession. If the directive had been false then the implicit variable session would be unavailable. If the session variable is not specified, then it defaults to “true.”

The import attribute describes the types that are available to the scripting environment. This attribute is used just as it would be in the Java programming language, i.e., a comma-separated list of ordinary import expressions. This list is imported by the translated JSP page implementation and is available to the scripting environment. Again, this is currently only defined when the value of the language directive is “java.”

JSP scripting elements

Once the directives have been used to set up the scripting environment you can utilize the scripting language elements. JSP 1.1 has three scripting language elements—declarations, scriptlets, and expressions. A declaration will declare elements, a scriptlet is a statement fragment, and an expression is a complete language expression. In JSP each scripting element begins with a “<%”. The syntax for each is:

<%! declaration %>

<% scriptlet %>

<%= expression %>

 

White space is optional after “<%!”, “<%”, “<%=”, and before “%>.”

All these tags are based upon XML; you could even say that a JSP page can be mapped to a XML document. The XML equivalent syntax for the scripting elements above would be:

<jsp:declaration> declaration </jsp:declaration>

<jsp:scriptlet> scriptlet </jsp:scriptlet>

<jsp:expression> expression </jsp:expression>

 

In addition, there are two types of comments:

<%-- jsp comment --%>

<!-- html comment -->

 

The first form allows you to add comments to JSP source pages that will not appear in any form in the HTML that is sent to the client. Of course, the second form of comment is not specific to JSPs—it’s just an ordinary HTML comment. What’s interesting is that you can insert JSP code inside an HTML comment and the comment will be produced in the resulting page, including the result from the JSP code.

Declarations are used to declare variables and methods in the scripting language (currently Java only) used in a JSP page. The declaration must be a complete Java statement and cannot produce any output in the out stream. In the Hello.jsp example below, the declarations for the variables loadTime, loadDate and hitCount are all complete Java statements that declare and initialize new variables.

//:! c15:jsp:Hello.jsp

<%-- This JSP comment will not appear in the

generated html --%>

<%-- This is a JSP directive: --%>

<%@ page import="java.util.*" %>

<%-- These are declarations: --%>

<%!

long loadTime= System.currentTimeMillis();

Date loadDate = new Date();

int hitCount = 0;

%>

<html><body>

<%-- The next several lines are the result of a

JSP expression inserted in the generated html;

the '=' indicates a JSP expression --%>

<H1>This page was loaded at <%= loadDate %> </H1>

<H1>Hello, world! It's <%= new Date() %></H1>

<H2>Here's an object: <%= new Object() %></H2>

<H2>This page has been up

<%= (System.currentTimeMillis()-loadTime)/1000 %>

seconds</H2>

<H3>Page has been accessed <%= ++hitCount %>

times since <%= loadDate %></H3>

<%-- A "scriptlet" that writes to the server

console and to the client page.

Note that the ';' is required: --%>

<%

System.out.println("Goodbye");

out.println("Cheerio");

%>

</body></html>

///:~

 

When you run this program you’ll see that the variables loadTime, loadDate and hitCount hold their values between hits to the page, so they are clearly fields and not local variables.

At the end of the example is a scriptlet that writes “Goodbye” to the Web server console and “Cheerio” to the implicit JspWriter object out. Scriptlets can contain any code fragments that are valid Java statements. Scriptlets are executed at request-processing time. When all the scriptlet fragments in a given JSP are combined in the order they appear in the JSP page, they should yield a valid statement as defined by the Java programming language. Whether or not they produce any output into the out stream depends upon the code in the scriptlet. You should be aware that scriptlets can produce side effects by modifying the objects that are visible to them.

JSP expressions can found intermingled with the HTML in the middle section of Hello.jsp. Expressions must be complete Java statements, which are evaluated, coerced to a String, and sent to out. If the result of the expression cannot be coerced to a String then a ClassCastException is thrown.

Extracting fields and values

The following example is similar to one shown earlier in the servlet section. The first time you hit the page it detects that you have no fields and returns a page containing a form, using the same code as in the servlet example, but in JSP format. When you submit the form with the filled-in fields to the same JSP URL, it detects the fields and displays them. This is a nice technique because it allows you to have both the page containing the form for the user to fill out and the response code for that page in a single file, thus making it easier to create and maintain.

//:! c15:jsp:DisplayFormData.jsp

<%-- Fetching the data from an HTML form. --%>

<%-- This JSP also generates the form. --%>

<%@ page import="java.util.*" %>

<html><body>

<H1>DisplayFormData</H1><H3>

<%

Enumeration flds = request.getParameterNames();

if(!flds.hasMoreElements()) { // No fields %>

<form method="POST"

action="DisplayFormData.jsp">

<% for(int i = 0; i < 10; i++) { %>

Field<%=i%>: <input type="text" size="20"

name="Field<%=i%>" value="Value<%=i%>"><br>

<% } %>

<INPUT TYPE=submit name=submit

value="Submit"></form>

<%} else {

while(flds.hasMoreElements()) {

String field = (String)flds.nextElement();

String value = request.getParameter(field);

%>

<li><%= field %> = <%= value %></li>

<% }

} %>

</H3></body></html>

///:~

 

The most interesting feature of this example is that it demonstrates how scriptlet code can be intermixed with HTML code, even to the point of generating HTML within a Java for loop. This is especially convenient for building any kind of form where repetitive HTML code would otherwise be required.

JSP page attributes and scope

By poking around in the HTML documentation for servlets and JSPs, you will find features that report information about the servlet or JSP that is currently running. The following example displays a few of these pieces of data.

//:! c15:jsp:PageContext.jsp

<%--Viewing the attributes in the pageContext--%>

<%-- Note that you can include any amount of code

inside the scriptlet tags --%>

<%@ page import="java.util.*" %>

<html><body>

Servlet Name: <%= config.getServletName() %><br>

Servlet container supports servlet version:

<% out.print(application.getMajorVersion() + "."

+ application.getMinorVersion()); %><br>

<%

session.setAttribute("My dog", "Ralph");

for(int scope = 1; scope <= 4; scope++) { %>

<H3>Scope: <%= scope %> </H3>

<% Enumeration e =

pageContext.getAttributeNamesInScope(scope);

while(e.hasMoreElements()) {

out.println("\t<li>" +

e.nextElement() + "</li>");

}

}

%>

</body></html>

///:~

 

This example also shows the use of both embedded HTML and writing to out in order to output to the resulting HTML page.

The first piece of information produced is the name of the servlet, which will probably just be “JSP” but it depends on your implementation. You can also discover the current version of the servlet container by using the application object. Finally, after setting a session attribute, the “attribute names” in a particular scope are displayed. You don’t use the scopes very much in most JSP programming; they were just shown here to add interest to the example. There are four attribute scopes, as follows: The page scope (scope 1), the request scope (scope 2), the session scope (scope 3—here, the only element available in session scope is “My dog,” added right before the for loop), and the application scope (scope 4), based upon the ServletContext object. There is one ServletContext per “Web application” per Java Virtual Machine. (A “Web application” is a collection of servlets and content installed under a specific subset of the server’s URL namespace such as /catalog. This is generally set up using a configuration file.) At the application scope you will see objects that represent paths for the working directory and temporary directory.

Manipulating sessions in JSP

Sessions were introduced in the prior section on servlets, and are also available within JSPs. The following example exercises the session object and allows you to manipulate the amount of time before the session becomes invalid.

//:! c15:jsp:SessionObject.jsp

<%--Getting and setting session object values--%>

<html><body>

<H1>Session id: <%= session.getId() %></H1>

<H3><li>This session was created at

<%= session.getCreationTime() %></li></H1>

<H3><li>Old MaxInactiveInterval =

<%= session.getMaxInactiveInterval() %></li>

<% session.setMaxInactiveInterval(5); %>

<li>New MaxInactiveInterval=

<%= session.getMaxInactiveInterval() %></li>

</H3>

<H2>If the session object "My dog" is

still around, this value will be non-null:<H2>

<H3><li>Session value for "My dog" =

<%= session.getAttribute("My dog") %></li></H3>

<%-- Now add the session object "My dog" --%>

<% session.setAttribute("My dog",

new String("Ralph")); %>

<H1>My dog's name is

<%= session.getAttribute("My dog") %></H1>

<%-- See if "My dog" wanders to another form --%>

<FORM TYPE=POST ACTION=SessionObject2.jsp>

<INPUT TYPE=submit name=submit

Value="Invalidate"></FORM>

<FORM TYPE=POST ACTION=SessionObject3.jsp>

<INPUT TYPE=submit name=submit

Value="Keep Around"></FORM>

</body></html>

///:~

 

The session object is provided by default so it is available without any extra coding. The calls to getID( ), getCreationTime( ) and getMaxInactiveInterval( ) are used to display information about this session object.

When you first bring up this session you will see a MaxInactiveInterval of, for example, 1800 seconds (30 minutes). This will depend on the way your JSP/servlet container is configured. The MaxInactiveInterval is shortened to 5 seconds to make things interesting. If you refresh the page before the 5 second interval expires, then you’ll see:

Session value for "My dog" = Ralph

 

But if you wait longer than that, “Ralph” will become null.

To see how the session information can be carried through to other pages, and also to see the effect of invalidating a session object versus just letting it expire, two other JSPs are created. The first one (reached by pressing the “invalidate” button in SessionObject.jsp) reads the session information and then explicitly invalidates that session:

//:! c15:jsp:SessionObject2.jsp

<%--The session object carries through--%>

<html><body>

<H1>Session id: <%= session.getId() %></H1>

<H1>Session value for "My dog"

<%= session.getValue("My dog") %></H1>

<% session.invalidate(); %>

</body></html>

///:~

 

To experiment with this, refresh SessionObject.jsp, then immediately click the “invalidate” button to bring you to SessionObject2.jsp. At this point you will still see “Ralph,” and right away (before the 5-second interval has expired), refresh SessionObject2.jsp to see that the session has been forcefully invalidated and “Ralph” has disappeared.

If you go back to SessionObject.jsp, refresh the page so you have a new 5-second interval, then press the “Keep Around” button, it will take you to the following page, SessionObject3.jsp, which does NOT invalidate the session:

//:! c15:jsp:SessionObject3.jsp

<%--The session object carries through--%>

<html><body>

<H1>Session id: <%= session.getId() %></H1>

<H1>Session value for "My dog"

<%= session.getValue("My dog") %></H1>

<FORM TYPE=POST ACTION=SessionObject.jsp>

<INPUT TYPE=submit name=submit Value="Return">

</FORM>

</body></html>

///:~

 

Because this page doesn’t invalidate the session, “Ralph” will hang around as long as you keep refreshing the page before the 5 second time interval expires. This is not unlike a “Tomagotchi” pet—as long as you play with “Ralph” he will stick around, otherwise he expires.

Creating and modifying cookies

Cookies were introduced in the prior section on servlets. Once again, the brevity of JSPs makes playing with cookies much simpler here than when using servlets. The following example shows this by fetching the cookies that come with the request, reading and modifying their maximum ages (expiration dates) and attaching a new cookie to the outgoing response:

//:! c15:jsp:Cookies.jsp

<%--This program has different behaviors under

different browsers! --%>

<html><body>

<H1>Session id: <%= session.getId() %></H1>

<%

Cookie[] cookies = request.getCookies();

for(int i = 0; i < cookies.length; i++) { %>

Cookie name: <%= cookies[i].getName() %> <br>

value: <%= cookies[i].getValue() %><br>

Old max age in seconds:

<%= cookies[i].getMaxAge() %><br>

<% cookies[i].setMaxAge(5); %>

New max age in seconds:

<%= cookies[i].getMaxAge() %><br>

<% } %>

<%! int count = 0; int dcount = 0; %>

<% response.addCookie(new Cookie(

"Bob" + count++, "Dog" + dcount++)); %>

</body></html>

///:~

 

Since each browser stores cookies in its own way, you may see different behaviors with different browsers (not reassuring, but it might be some kind of bug that could be fixed by the time you read this). Also, you may experience different results if you shut down the browser and restart it, rather than just visiting a different page and then returning to Cookies.jsp. Note that using session objects seems to be more robust than directly using cookies.

After displaying the session identifier, each cookie in the array of cookies that comes in with the request object is displayed, along with its maximum age. The maximum age is changed and displayed again to verify the new value, then a new cookie is added to the response. However, your browser may seem to ignore the maximum age; it’s worth playing with this program and modifying the maximum age value to see the behavior under different browsers.

JSP summary

This section has only been a brief coverage of JSPs, and yet even with what was covered here (along with the Java you’ve learned in the rest of the book, and your own knowledge of HTML) you can begin to write sophisticated web pages via JSPs. The JSP syntax isn’t meant to be particularly deep or complicated, so if you understand what was presented in this section you’re ready to be productive with JSPs. You can find further information in most current books on servlets, or at java.sun.com.

It’s especially nice to have JSPs available, even if your goal is only to produce servlets. You’ll discover that if you have a question about the behavior of a servlet feature, it’s much easier and faster to write a JSP test program to answer that question than it is to write a servlet. Part of the benefit comes from having to write less code and being able to mix the display HTML in with the Java code, but the leverage becomes especially obvious when you see that the JSP Container handles all the recompilation and reloading of the JSP for you whenever the source is changed.

As terrific as JSPs are, however, it’s worth keeping in mind that JSP creation requires a higher level of skill than just programming in Java or just creating Web pages. In addition, debugging a broken JSP page is not as easy as debugging a Java program, as (currently) the error messages are more obscure. This should change as development systems improve, but we may also see other technologies built on top of Java and the Web that are better adapted to the skills of the web site designer.

Exercises

12.Create a JSP page that prints a line of text using the <H1> tag. Set the color of this text randomly, using Java code embedded in the JSP page. If you do not have an existing JSP container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run JSPs.

13.Modify the maximum age value in Cookies.jsp and observe the behavior under two different browsers. Also note the difference between just re-visiting the page, and shutting down and restarting the browser. If you do not have an existing JSP container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run JSPs.

14.Create a JSP with a field that allows the user to enter the session expiration time and and a second field that holds data that is stored in the session. The submit button refreshes the page and fetches the current expiration time and session data and puts them in as default values of the aforementioned fields. If you do not have an existing JSP container, you will need to download, install, and run Tomcat from jakarta.apache.org in order to run JSPs.
Custom Tags

Custom Tags provide you with the full power of the Java language with almost seamless integration into your presentation layer[6].

A custom tag is a homemade JSP tag. In order to fully understand the implications of this, you first need to be clear about what a tag is.

The chances are that you will have made good use of tags long before reading this chapter. Very few developers these days work in environments which are completely divorced from the Internet, and that means you have probably had at least some basic HTML or XML exposure.

In a mark-up language, like HTML, or XML, the structure is embedded within the data. This allows the parser, e.g. a browser, to interpret instructions for displaying or storing the information.

The main mechanism for this is the tag.

At its most simple, a tag, like for instance, <b> just tells the reader (in this case, a web browser) to apply some formatting to the data. With this particular tag, it would be to display the data (which is plain text) in bold format. Every tag should have a start and end point i.e. in this case </b> which allows the parser of the mark-up language to identify a portion of content, by determining which of the data lies between the start and end tags. This allows some structure or format to be applied to that data. In this case any text between the start and end tags would be displayed in bold font.

The other important mechanism for applying this structure to the data, is the attribute, making the tag slightly more complicated, like the “table” tag in HTML, for example. A table tag can have a border attribute e.g. <table border=”3”>, this attribute value, border, applies to the data which appears after the <table> tag and before the </table> tag. This data found in between the two tags is referred to as the body of the tag. All data in the body of this tag would be displayed in a table, and the border around each element in the table would be of width “3”.

In the case of the table tag in HTML, it gets more complicated still, because the body of the tag can contain other tags as well as data, allowing nesting. Thus you can define a table row within a table, and table data (or columns) within a row. This allows for very good structuring of the data.

e.g.

<table border=”3”>

<tr>

<td>black</td>

<td>white</td>

</tr>

</table>

 

This defines a table, with a border of size three, one row and two columns, containing data black and white. The words black and white are the data items which make up the body of the <td> or table data tag. The <tr> or table row tag with its embedded <td> tags and data make up the body of the table tag. You can clearly see that the relationship between these tags is important. The table row tag, <tr>, doesn’t have much meaning outside of a table tag, and similarly, the table data tag, <td>, has no meaning outside of the table row tag. We refer to the table tag as being the parent of the table row tag, and the table row tag as being a parent of the table data tag.

XML introduces the notion of namespaces which allow you a little more flexibility again when naming tags. (see the XML chapter “” for more detail). If you want to create your own HTML-type mark up language, you could define a namespace called mytags and define your tag within it, thus having a tag <mytag:table> and </mytag:table>. Put simply, this would mean that there would be no confusion between the original HTML table tag and your own, newly defined one. Your table tag is in its own namespace. This means that you can use consistent naming conventions, e.g. call a “table” a “table”, rather than “myspecialtable”, without having to worry about it clashing with the HTML standard definition.

Custom tags have all of the above properties. They have bodies that can either contain data, or might be empty and they have properties that can be applied to the body of the tag. They also use namespaces to allow consistent naming conventions. You will use some or all of these features, depending on what is appropriate for your application.

What do custom tags give us?

Power: As with JavaBeans, the real programming problems like querying databases or making complex calculations etc. can all be done in Java code with the full power of the Java libraries behind you. JSP custom tags also allow you to make objects available to a web page author via fragments of Java code, or “scriptlets”, so with careful design, you can make a lot of functionality available via a simple and neat interface.

Consistency: Unlike JavaBeans, the presentation code is neatly separated from the business logic code, the need for scriptlets is reduced and more development is done using HTML type tags. It is therefore easier for a web page author to understand, and much easier to read and debug.

Encapsulation: The business logic, is now defined in Java classes that can solve complex programming problems like network communication or database querying. This code can be unit tested in isolation, and finally presented as simple tags, ideal for a web author to use when getting on with building the web application.

Reuse: Tags are stored in libraries which are perfect entities for re-use in web development. Tag libraries typically contain a collection of tags of similar or connected functionality. The library can easily be included by a web page author needing that functionality. A tag is even easier to use than your average Java method!

At this point, if you remember the earlier discussions about using Java beans from JSP in the JSP chapter, you might be asking yourself why this is necessary. Javabeans in JSP also help us to separate the presentation layer from the business logic. Beans can be stored in libraries too, they are reusable, have a consistent interface and can even communicate with scriptlets, so why do we need tags?

The answer is quite subtle. Beans are indispensable to the application developer and are very helpful if used in the right way. In fact, as we shall see, beans and custom tags complement each other very nicely. The difference, though, is that beans are components, and custom tags are essentially actions, or handlers. This needs to be understood well and carefully considered in your design if it is not to lead to the incorrect use of beans.

Beans, like any other components, have properties, methods and internal state. What makes using beans with JSP so easy is that the interface between JSP pages and Java beans is designed specifically for getting and setting the bean properties. As long as you have named your bean methods according to the Javabeans convention, you can access these properties very simply via JSP.

Let’s revisit Javabeans. Consider a person bean that has two properties, name and age. In order to access these via JSP tags, you would need to use the

<jsp:usebean …> action, and the

<jsp:getProperty name=”beanName” property=”requiredProperty”> action for each of these two values. Should you want to put these in the row of an HTML table, you would have to specify

..

<table>

<tr>

<td><jsp:getProperty name=”personbean” property=”name”></td>

<td><jsp:getProperty name=”personbean” property=”age”></td>

</tr>

</table>

..

 

This is good-looking, consistent HTML type code. It is very manageable at the moment, but will get more ugly and “bloated” as you use beans with more properties. You might have five beans, with five properties each, and instantly you would have twenty five lines to type. In a real application you are very unlikely to have as few as two properties, as we do in the example above. What is needed here is clearly some programming so we don’t have to do so much typing and create so much source code.

One of the great strengths of using JavaBeans with JSP is also a limitiation. Because of the JSP getProperty and setProperty tags, it is very easy to get and set the bean properties. Remember, these are short cuts for using the set and get methods. But, if you are trying to call other methods on the bean, you have to do it directly and you are confined to using scriptlet code or expression code, using the <% %> tags. This starts to make the code look a little ugly and inconsistent. Consider the fragment.

<ol>

<% while (list.hasNextElement()) {

list.nextElement();

%>

<li><jsp:getProperty name=”list” property=”currentElement”></li>

<% } %>

</ol>

 

In the example above, notice that to iterate through a simple loop, and display items from a list (using an imagined list bean which iterates through its data) the first two lines of the Java scriptlet code have to be put in scriptlet brackets. The jsp:getProperty tag which allows the use of HTML type brackets gives consistent looking presentation code, but then the close brace of the while loop has to use the scriptlet brackets, <%}%> again. Two tags are now enclosing a single close bracket and as you can see, it is not very nice to look at. If you were trying to debug it, it might be very difficult to work out where one loop ends and the next starts. JSP debuggin tools are improving but still leave a lot to be desired. Add to this the fact that it is almost impossible to arrive at a consistent convention for indenting your brackets and you can see it is potentially messy.

Some scriptlet code is inevitable, but too much is undesirable. As the complexity of the code increases, so the combination of HTML tags, JSP directives, actions and scriptlet code, each with its own bracket notation, further increases the complexity of the code, making it difficult to read and maintain.

How can this be prevented? It is very tempting to write a bean that returns values that are already in tags. This would at first glance appear to remove a great deal of the HTML code and keep the documents shorter and simpler. Implemented like this, a bean could just return a string value from the get method that already has formatting tags in it and our data would display itself nicely on the screen.

As attractive as this sounds, it turns out to be a very bad idea as you will see.

Consider a bean which has a String method which returns a table row of data including the tags. It might look something like this..

public class PersonDataBean {

private int age;

private String name;

String getCurrentRow() {

return “<tr><td>” + name + “</td><td>”+ age +

“</td></tr>”;

}

}

 

The getCurrentRow() method now returns a string which displays a table row, and the HTML which displayed the person in a table, two examples above, becomes simply:

..

<table>

jsp:getProperty name=”personbean” property=”currentRow”>

</table>

..

 

This might makes for shorter HTML coding but it is a poor design approach for a number of reasons. For one thing, it creates a very tight cohesion between the data and its presentation. That tends to cloud one’s ability to think of the bean as an object, and clutter one’s design.

From a programming perspective, it is even worse. A programmer using this bean would get the results back in a table whether or not it was appropriate. The layout and the field order wouldn’t be changeable programmatically and although a stylesheet could be used, to change the style of the output, it would not be possible to do anything more specific, (like make one column appear in bold text, for example).

This means that the web page author using the bean is now limited to presenting data in a format defined by the bean developer. The bean developer might be an excellent database developer, but might not be the person responsible for presenting the final layout. In fact, it is very likely that the layout requirements are not even known at the time of the bean’s development. The bean should have been written with reuse in mind, and no one designing a web page would appreciate having to work with a pre-determined layout.

So, although this appears to solve the problem of increased code complexity, it in fact detracts enormously from the code’s re-usability.

You will always come across these layout issues with dynamic web content. When developing web pages, you constantly need layouts or presentations that are dependent on certain program conditions (like variable length tables, different colors for values within certain ranges etc.) This is the dynamic behavior one would expect from a web page, but this behavior makes the code hard to read by requiring scriptlet programming with funny brackets embedded in the code.

Scriptlet code obviously can’t be removed from JSP pages completely, and nor would you want that. In fact, writing scriptlets, with JavaBeans, (or any kind of scripting language with any kind of component) is an excellent way to build an application, as long as it is not abused. So far, we have highlighted two problems, the need to separate presentation code from business logic, and the need to prevent the kind of complexity that creeps into our presentation code when we mix HTML and scriptlet code. We can also see, that as useful as JavaBeans are, they are not always the solution to these problems and may lead to bad design. That is where the custom tag comes into play.

A JSP tag can be thought of as an action, or a handler for a number of events. These events are actually triggered by the JSP container reading the JSP source and parsing each tag. The different methods that correspond to these events, are amongst others, doStartTag() and doEndTag(), the point at which they are called should be obvious from their names.

There are of course more than just 2 methods you will need to create custom tags, but when writing our own tags we have various classes and interfaces we can use which provide us with more or less functionality depending on our needs.

Let us have a look at a simple tag, a “Hello World” tag will do nicely.

What would be ideal is a tag that would display Hello World. The tag should look something like this:

<tijtags:helloworldtag/>

 

Note that its namespace is tijtags and its name is helloworldtag. Note also that there is a forward slash at the end of the tag name. This means that the tag is a start and end tag all in one. This makes use of the short hand notation defined in XML. It could also be written like this:

<tijtags:helloworldtag>

</tijtags:helloworldtag>

 

In either case, the tag clearly has no body, and as you might have guessed, needs the very minimum of programming to make it work. You will need to tell the JSP container how to find your Java classes, though, so in order to do this bare minimum the following tasks need to be performed:

1.       Write a class that implements the Tag interface, found in the package javax.servlet.jsp.tagext.Tag

2.      implement the methods “setPageContext(), doStartTag() and doEndTag()”

3.      create a TLD file, or tag library descriptor for the tag.

Let’s take care of the first 2 tasks:

package cx2.tags;

import javax.servlet.jsp.PageContext;

import javax.servlet.jsp.tagext.Tag;

import javax.servlet.jsp.JspException;

import java.io.IOException;

 

public class HelloWorldTag implements Tag {

PageContext pageContext;

public void setPageContext(PageContext pageContext) {

this.pageContext = pageContext;

}

public void setParent(Tag parent) {}

 

public Tag getParent() { return null; }

public int doStartTag() throws JspException {

try {

pageContext.getOut().println("Hello World!");

} catch(IOException ioe) {

ioe.printStackTrace();

}

return Tag.SKIP_BODY;

}

public int doEndTag() throws JspException {

return Tag.EVAL_PAGE;

}

public void release() {}

}

 

You will notice that there is no constructor defined, which means that this class has a default, parameterless constructor. This is one of the prerequisites of writing a tag handler class.

You will notice that a property called pageContext has been defined, which is of type PageContext, found in the javax.servlet.jsp package. This is initialized in the method setPageContext(). The container calls this automatically and passes through a reference to the PageContext when it first reads the tag. This reference has to be stored because it is needed later on. The JSP Tag Extension specification guarantees that the setPageContext() method will be always be called before the doStartTag() method.

Next, the method doStartTag() is implemented. This method is where all of the work for this particular tag is done.

Using the pageContext object, which has already been initialized, the JSP output stream is requested. This is the output stream that echoes back to the browser which is reading the JSP page, and its println() method is called, in exactly the same way it would be if you were using System.out.

Notice that the doStartTag() method returns the value Tag.SKIP_BODY. This tells the container that it should ignore anything between the start and end tags. Since this tag does not even have a body this is all that is needed for now.

The doEndTag() method is implemented simply by returning the value Tag.EVAL_PAGE. This tells the container to continue processing the JSP page as it was until it came across this tag. Again, with a tag this simple, that is all that is needed.

There are some other methods of the Tag interface that must be implemented, but only the three mentioned above are important for now.

All that is left to do now for the HelloWorld tag, is to describe to the container what your new tag is called and which class will be implementing it. To do this you need to write a tag library descriptor, an XML file which the container uses to gain information about the tag or tags you have defined. This file is saved with a .tld extension and is placed on your server with your JSP files.

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE taglib

PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"

"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

<taglib>

<tlibversion>1.0</tlibversion>

<jspversion>1.1</jspversion>

<shortname>htl</shortname>

<uri>http://www.eckelobjects.com/tagtut/tags1.0</uri>

<info> Library of tags on increasing complexity to demonstrate the use of tag libraries.</info>

<tag>

<name>helloworldtag</name>

<tagclass>cx2.tags.HelloWorldTag</tagclass>

<bodycontent>empty</bodycontent>

<info> echo's hello world to the browser.

</info>

</tag>

</taglib>

 

Don’t be intimidated by all of this. The tags in this file are less arcane than they seem. They will be presented in greater detail later on, for now, all you need to worry about is the tag tag. This tag, unsurprisingly, tells the container things it needs to know about the new tag that you have just created. There are four things that are important with this particular tag; its name, which class contains the tag implementation, what kind of its body content it should have and lastly, a little description of what the tag does. The description should be short and clear, the kind of thing you might expect to see on the toolbar of a GUI tool giving you the option to use this tag in your application.

To use the new tag in a JSP page you would specify something like this:

<%@ taglib uri="/tijtags.tld" prefix="tijtags"%>

<b>

<tijtags:helloworldtag/>

</b>

 

That is all you need to do to send “Hello World” back to the browser!

The first line is the JSP taglib directive. The uri attribute tells the container that your tag library descriptor can be found in a file called “tijtags.tld” in the current directory. The prefix attribute tells the container what namespace you are going to use to refer to your tags.

The <b> tag is just for decoration and to emphasize the point that you can use HTML freely with JSP tags. This combination becomes very powerful when you are writing iterative tags.

Using Tags and JavaBeans

The “hello world” tag was not a very powerful tag. It certainly didn’t save anyone any effort. In this case, it would have course been easier just to type the words “Hello World” into the text yourself. Let’s look at an example that does a little bit more in the way of programming, and at the same time illustrates how tags can make objects available to the JSP page.

This time, the intention is to create a greeting tag that is slightly more useful than “hello world”, and therefore a little more general. Imagine a greeting tag, that when used would create a greeting object and make it available on the JSP page. If this object, a Javabean, had a greeting property that produced a random greeting, such as “Hi there!” or “Hello”, you might use it something like this..

<tijtags:greetingtag id="randomgreeting"/>

<jsp:getProperty name="randomgreeting" property="greeting"/>

 

This is a little bit more involved than the last tag. Let’s look closely at the differences.

Firstly, although this is another bodiless tag, it has an attribute, called id. The tag reference above passes the string “randomgreeting” through to the tag.

Next there is a JSP action, getProperty which appears to be requesting the value of the property “greeting” from a Javabean called “randomgreeting”. It is no coincidence that the name of this bean is the same as the value of the “id” attribute, you might have guessed by now that it is the tag that has enabled the getProperty action to be used on a JavaBean by this name. Usually you would expect to see the JSP action <jsp:useBean... declared at the top of the JSP file before you could use the getProperty action to access a bean, but in this case it is the tag that has effectively defined the bean and made it accessible from the page.

The tasks needed for this tag to work would be the following:

1.       Create a Javabean with a property greeting, and a get method that returns a random greeting string, like “Hi there!” or “Hello”.

2.      Write a class that implements the Tag interface.

3.      Give the class a setId() method to allow the container to set the id attribute.

4.      Create an instance of the greeting JavaBean and store it in the page context.

5.      create a TLD or tag library descriptor file for the tag.

There are a few more things to do this time, but this is still pretty straightforward. You already know how to write a JavaBean, but here is a minimal one that will be sufficient.

package cx2.tags;

 

public class GreetingBean {

String greetings[] = {"Hello!","Howdy!","Hi There!","How do you do?"};

public String getGreeting() {

return greetings[(int)(Math.random()*greetings.length)];

}

}

 

The idea is that the tag class will make an instance of this JavaBean available so that the JSP page author can use the getProperty action on his page.

Here is the Tag class itself.

package cx2.tags;

import javax.servlet.jsp.tagext.Tag;

import javax.servlet.jsp.PageContext;

import javax.servlet.jsp.JspException;

 

public class GreetingTag implements Tag {

private String id;

private PageContext pageContext;

public GreetingTag() {

}

public void setId(String id) {

this.id = id;

}

public void setPageContext(PageContext pageContext) {

this.pageContext = pageContext;

}

public void setParent(Tag parent) {

}

public Tag getParent() {

return null;

}

public int doStartTag() throws JspException {

pageContext.setAttribute(id, new GreetingBean(),

PageContext.PAGE_SCOPE);

return Tag.SKIP_BODY;

}

public int doEndTag() throws JspException {

return Tag.EVAL_PAGE;

}

public void release() {

}

}

 

You will notice that there is a setId() method. This is not on the Tag interface, but the container will call this method anyway. It will be passed the value of the attribute id as it is specified in the JSP file. This highlights another interesting relationship between JavaBeans and tags. A tag which has attributes must have JavaBean style properties which obey the correct naming convention and must match the attribute names. These properties will be set automatically by the container, using the JavaBeans mechanism built into Java. Two additional rules not normally associated with JavaBeans, but which are important here, are that you may only have one setter method per attribute, and you should not call a setter method yourself, i.e. from anywhere else in the tag code.

As before, most of the work for this tag is done in the doStartTag() method. It is worth paying slightly closer attention this time, to the fact that the doStartTag() and doEndTag() methods may each throw a JspException. This can be ignored for the time being, but if an exception occurs in your tag implementation, a JspException will be thrown which might end up being displayed on the client browser window, which is rarely desirable. A technique for handling this will be explained later.

This time the doStartTag() method uses the setAttribute() method on the pageContext object to create an instance of the greeting bean, and store it in the pageContext, specifying its name and its scope. The name comes from the id attribute, and the scope is the familiar constant PAGE_SCOPE, a final property of the class PageContext. If that sounds complicated, think of it in two parts. The tag class has already been passed the name via the setId() method, and stored it in the variable id. Now it creates a GreetingBean object and adds it to the pageContext, giving it the name stored in the id variable. This is the programmatic equivalent of the JSP action: <jsp:useBean...>. The JavaBean is now available for use with the <jsp:getProperty..> action on the JSP page.

All that is left to do now, is to create the TLD file. Since one exists already, all you need to do is add another tag tag before the final </taglib>

..

<tag>

<name>greetingtag</name>

<tagclass>cx2.tags.GreetingTag</tagclass>

<bodycontent>empty</bodycontent>

<info> Adds a HelloWorld Bean to the page context. </info>

<attribute>

<name>id</name>

<required>true</required>

</attribute>

</tag>

..

 

Notice that in this tag tag there is a subtag, called attribute. The name subtag of attribute tells the container what that attribute is called, and the required subtag tells the container whether that attribute is mandatory or not. Mandatory subtags must be specified, i.e in this case,

<tijtags:greetingtag id="randomgreeting"/>

 

otherwise the container will throw an exception when an attempt is made to load the JSP page.

In order for your tag to properly declare and initialize an attribute, the name of that attribute, the name in the tag library descriptor file and the name of the JavaBean property (and setter methods) must all be the same.

To use the random greeting tag, and the bean object it creates, you would need to specify the tag:

<tijtags:greetingtag id="randomgreeting"/>

 

which would create a JavaBean in the page context for you to use.

Then you would need to get the property from the bean:

<jsp:getProperty name="randomgreeting" property="greeting"/>

 

which would echo the greeting back to the browser. This latter line can then be used as often as you like on the JSP page after the declaration above it. Each time it appears, it will produce a different greeting. Refreshing the page in your browser will also produce new random greetings, because every time the container processes the getProperty action, it will make a call to the method getGreeting() on the GreetingBean class, which will return a new, random greeting. (Note that that the use of a get method on a JavaBean that returns a random value might offend some purists. It is used here as a good demonstration of working with beans.)

Tags that manipulate their body content

Up until now, the two tags that we have looked at ignore their body content, or to be more accurate, they are defined as not having any body content at all. This kind of tag is very simple, and fairly limited in its usefulness, although, as you can imagine, making a bean available to the page author can be pretty useful. Tags which manipulate their body content are generally much more powerful, if slightly harder to program.

Let’s look at a tag that actually interacts with its body content.

This time, suppose you wanted to generate a tag that gives you a nice looking drop capital for your text. For example..

It was a dark and stormy night...

This can be done in HTML in a somewhat primitive fashion, by using heading text and tables, which serves our purposes, but rather than write out a table every time you want a drop capital, why not create a tag to do it for you? Ideally it should be possible to do something like the following:

<tijtags:dropcapitaltag color = "black">

 

it was a dark and stormy night, the rain fell in torrents -- except at occasional intervals, when it was checked....

</tijtags:dropcapitaltag>

 

The color attribute would be a nice thing to have so that a giant capital could be displayed in the specified color.

To create this tag,. the important tasks you would need to perform would be the following:

Write a class that implements the Tag interface.

Give the class a setColor() method to allow the container to set the color attribute.

Write the code that reads in the body content and inserts the relevant HTML tags so that the first letter is displayed as a drop capital.

Create a tag library descriptor file for the tag.

Lets have a look at the code for this tag:

package cx2.tags;

import javax.servlet.jsp.tagext.BodyTagSupport;

import javax.servlet.jsp.tagext.TryCatchFinally;

import javax.servlet.jsp.JspException;

import java.util.StringTokenizer;

public class DropCapitalTag extends BodyTagSupport implements TryCatchFinally {

String color = "black";

public void setColor(String color) {

this.color = color;

}

public int doAfterBody() throws JspException {

int period;

StringBuffer result = new StringBuffer();

String body = bodyContent.getString().trim();

result.append("<table><tr><td><h1><font color=\""+color+"\">");

result.append(Character.toUpperCase(body.charAt(0)));

result.append("</font></h1></td><td>");

period = body.indexOf('.');

result.append (body.substring(1, period+1));

result.append ("</td></tr></table>");

result.append (body.substring(period +1, body.length()));

try {

if (result.length() > 0) {

bodyContent.clearBody();

bodyContent.println(result.toString());

}

bodyContent.writeOut(bodyContent.getEnclosingWriter());

} catch(Exception e) {

e.printStackTrace();

}

return SKIP_BODY;

}

public void doCatch(Throwable t) {

System.out.println(

"An error occurred with the message" +

t.getMessage());

}

public void doFinally() {}

public void release() {

color = "black";

}

}

 

The first thing you will notice is that unlike the previous tags, this one does not implement the Tag interface. This is because when you need to manipulate your body content, it is more useful to extend the BodyTagSupport class, which takes care of storing the pageContext object and providing default implementations of the doStartTag() and doEndTag() methods. This class implements the Tag interface so you don’t have to.

The doAfterBody() method of this tag is where everything happens. You will see that this class references a handle called bodyContent. This is a reference to the BodyContent object, which is found in the javax.servlet.jsp.tagext package. This is provided by the container via a setBodyContent() method and is something else we get for free when we extend the BodyTagSupport class. This container will always provide this object before it calls the doInitBody() method, so you are always guaranteed that you have a valid BodyContent object in your doInitBody(), doAfterBody(), doStartTag and doEndTag() methods.

The BodyContent object is an encapsulation of the body content of this particular tag, it contains all of the text from the JSP page that is in between the start and end tag. It is a quite a lot like an output stream, but with some extra functionality. This object gives you access to the JSP text. This is what the drop capital tag does, and is what you will need to do if you are going to manipulate the body content. You can get the entire body as a string, via a call to the getString() or as a stream, using the getReader() method if that is more appropriate for your needs.

In this example a simple algorithm is used to trim off any white space that comes from the JSP source, capitalize the first letter, and add a few HTML tags, to format it into a drop capital effect.

After the HTML tags have been added (using a StringBuffer object to append strings) there is one new, long string which needs to replace the old body text. To do this, the method clearBody() on the BodyContent is called first. If you don’t do this, the body content will appear twice on the browser. Next we use the println() method to add our new string to the BodyContent, and finally, the writeOut() method is called. This method tells the BodyContent object to direct its output back to the browser, but you will notice that the method takes a JspWriter as a parameter, and in this case the writer is obtained by a call to getEnclosingWriter(). You have to do this, because the body content object only includes the body of your particular tag and you need access to the output stream that directs output back to the client’s browser.

If you look closely, you will see that the release() method does nothing. This method will be called by the container to release its state, so it is best used to tidy up any resources that you might have used in the tag. Be aware that there might be multiple calls to doStartTag() and doEndTag() in between each call to release, so this is not the method to use if you want to perform an operation each time the tag processing has completed. If you want to do that, you are better off using the doFinally(), described in the next paragraph.

You might have noticed that this class implements the TryCatchFinally interface, which is in the javax.servlet.jsp.tagext package. When the container sees that your tag implements this interface, it knows to put a try/catch block around your tag handler, and passes the processing on to you if an exception occurs. This means that you have the ultimate control over the exception handling code, and if you wish, you can prevent weird exceptions being displayed on the browser window.

The methods on the TryCatchFinally interface are doCatch() and doFinally() and they correspond to the catch and finally keywords in Java. The doCatch() method is called if there is an exception with the exception passed as a parameter, and the doFinally() is always called, regardless of what happens in the other tag handler methods. In this example, if there is an exception, the message is just displayed on the console on the server, not the client browser. This is a slightly lazy design approach used to demonstrate the interface. The algorithm used here is not very robust so an exception could easily occur. In the doFinally() method above, the color is set to black so that that is always the default value.

Use this interface with caution. Its chief purpose is to give you more control when handling resources, like open streams etc. Remember that exceptions are all about recovery and it might be more appropriate to handle exceptions in your doAfterBody(), or doInitBody() methods and produce some meaningful output to display on the client’s browser, for example an error page which asks the user to re-enter some details. You might just want to forward the browser to a generic error page if that is more appropriate.

Now that you have seen how the class is implemented, lets look at the tag entry in the tag library descriptor file. It looks like this:

..

<tag>

<name>dropcapitaltag</name>

<tagclass>cx2.tags.DropCapitalTag</tagclass>

<bodycontent>JSP</bodycontent>

<info> Transforms text to have a drop capital.

</info>

<attribute>

<name>color</name>

<required>false</required>

</attribute>

</tag>

..

 

Notice that the color attribute is not required. As you can see in the source code, the tag will default to black if no color is specified, so it is not enforced here.

That is all you need to do to manipulate the body of a tag!

Tags that iterate

One of the most powerful features of custom tags in JSP is that they can iterate like a for loop or while loop in Java and produce dynamic output this way.

Lets look at a more complicated tag that actually iterates over its body content, producing variable output depending on its attributes.

Imagine you wanted to generate a list of prime numbers (numbers divisible only by themselves and 1) and display them in an HTML unordered list. What you would really like is a tag that would generate all of the prime numbers within a specified range, and allow you to access each value and put it in your preferred list format. Since you don’t know how many numbers there will be within the given range, and there could be quite a few, you don’t want to have to type out the HTML list item tags, <li> and </li>, for each one. Assuming that your tag is called primetag, you would want your JSP file to look something like this:

<ul>

<tijtags:primetag start = "1" end= "25">

<li><%=value%></li>

</tijtags:primetag>

</ul>

 

Notice that there is a start and end <ul> HTML tag round the entire block. **(An HTML lesson is beyond the scope of this chapter, but briefly, this defines an unordered list and within the body of this tag, any data found between a <li> and </li> pair, will be displayed as a bulleted list item.)

Next you will notice the now familiar tijtags namespace reference, specifying the new custom tag, primetag, with two attributes, start and end.

In this tag, unlike the two previous tags, not only is an end tag specified separately, </tijtags…, but there is also some body content between the start and end tags. That is what makes this tag so interesting. The <li> tags are just HTML as explained above, but in between them, enclosed in JSP scriptlet brackets, is a reference to the variable value.

There are two very powerful properties of this custom tag. Firstly it has made the variable value available to us for use in a scriptlet, or simple JSP expression, like <%= value%>. Secondly, everything within the body of our custom tag is repeatedly processed and added to the output stream to be presented back to the browser. This means that not only will the tag above keep looping until it has found every prime number within the specified range, but it will display the <li> and </li> tags as well as the value of the variable for each number! In other words, the JSP code above will be all you need to do to display the prime number values in a neat list. And you don’t even need to know how many numbers there are going to be.

This has suddenly become very useful. It is a tag that helps reduce the need to embed scriptlet code in our neat tags, but gives us full programming functionality, like looping.

In order to implement such a tag you need to perform the following tasks:

Create (or locate) a class with the appropriate logic for generating prime numbers.

Write a tag class which implements the relevant tag interface.

Write setter methods for the start and end properties.

Program a looping mechanism to process the body content and for each iteration, make a variable available to the JSP page with the current prime number value.

Create a TLD or tag library descriptor file for the tag.

You can find the PrimeUtilities class on the CD (??)or in appendix (??). This example makes use of the sievePrimes() method, which finds prime numbers. There are many resources on the Internet for finding such algorithms if you are interested in this type of thing.

Let’s look at the actual tag class.

package cx2.tags;

import java.util.ArrayList;

import java.util.Iterator;

import javax.servlet.jsp.tagext.BodyTagSupport;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.PageContext;

import com.bruceeckel.PrimeUtilities;

 

public class PrimeNumTag extends BodyTagSupport {

private int start = 0;

private int end = 0;

ArrayList primeList;

Iterator primeIterator;

public void setStart(int start) {

this.start = start;

}

public void setEnd(int end) {

this.end = end;

}

public int doStartTag() throws JspException {

try {

primeList = PrimeUtilities.sievePrimes(start, end);

primeIterator = primeList.iterator();

} catch (Exception e) {

e.printStackTrace();

return SKIP_BODY;

}

return EVAL_BODY_BUFFERED;

}

public void doInitBody() throws JspException {

try {

if (primeIterator.hasNext()) {

pageContext.setAttribute("value",

primeIterator.next());

}

} catch (Exception e) {

e.printStackTrace();

}

}

public int doAfterBody() {

try {

if (primeIterator.hasNext()) {

pageContext.setAttribute("value",

primeIterator.next());

return EVAL_BODY_BUFFERED;

} else {

bodyContent.writeOut(bodyContent.getEnclosingWriter());

return SKIP_BODY;

}

} catch (Exception e) {

e.printStackTrace();

return SKIP_BODY;

}

}

public void release() {

primeList = null;

start = 0;

}

 

The first thing you will notice is that this tag class doesn’t implement the Tag interface directly any more. This is because the tag has a little more functionality now and it is more useful to extend the BodyTagSupport class. This class implements the Tag interface and provides some extra functionality. The various Tag classes and interfaces will be discussed in greater detail later.

This class again makes use of the the PageContext object, supplied by the container using the setPageContext() method. Since this tag class inherits from BodyTagSupport, it is no longer necessary to store the pageContext object yourself. That will be taken care of by the parent class. This is a good example of reuse using inheritance.

The doStartTag() method has now become the first method you need worry about. In the implementation above, the PrimeUtilities class is used to obtain and store an Iterator object which contains the prime numbers. Note that unless an exception occurs, this method will return the value EVAL_BODY_BUFFERED, unlike the two previous examples which returned SKIP_BODY in all cases. As you might have guessed, this is the key to implementing a tag which processes its body content.

Next you will notice that the method doInitBody(), has been implemented. This method is called by the JSP container before the body of your tag has been processed, and is the method in which you will perform any initialization code required before processing the body content. In the previous example, this wasn’t necessary, as there wasn’t really anything to do. In the example above, however, this method queries the prime number iterator to make sure there are prime numbers available in the list, and if there are, the method setAttribute()is used to add the first available prime number value to the page context. As you can see, the value is associated with the name value.

You came across a similar form of the setAttribute() earlier, but this time you will notice it has only two parameters that is because this class makes use of the default scope which is PAGE_SCOPE. The job of making this object available to the JSP page is actually in part the responsibility of a special class called a TagExtraInfo class which will be discussed a bit later on. The looping construct is now initialized and provided no exceptions have occurred, the tag will read in the body text at this point.

The method doAfterBody()is not dissimilar to the doInitBody() method. It checks to see whether there are any more prime numbers in the iterator, and if there are, it adds the available prime number value to the page context. If there are no more prime numbers available, it writes the body content of the tag to the output stream that echoes it back to the browser.

If you look closely you will see that this is not quite straightforward. The object referenced by bodyContent is in fact an instance of the BodyContent object, found in the javax.servlet.jsp.tagext package. This is an encapsulation of the body content of this particular tag, and is a bit like an output stream, but with some extra functionality. If the PrimeNumTag object had not extended the BodyTagSupport class, then a setBodyContent() method would have had to be implemented and the bodyContent object stored “manually” in the same way that we did with the pageContext object in the previous examples.

Because the body content object only includes the body of your particular tag, you will need to make a call to the getEnclosingWriter() method in order to get to the right output stream.

What actually causes the loop to iterate is the return value. If the doAfterBody() method returns the constant EVAL_BODY_BUFFERED, then the container will process the body of the tag and call that method again. If the method returns SKIP_BODY, then the loop will terminate, and provided you have sent your body content to the enclosing writer stream, your values will be displayed on the browser screen.

All that is necessary now, is to create the tag entry in the TLD file.

<tag>

<name>primetag</name>

<tagclass>cx2.tags.PrimeNumTag</tagclass>

<bodycontent>JSP</bodycontent>

<info> Generates Prime Number sequences </info>

<attribute>

<name>start</name>

<required>false</required>

</attribute>

<attribute>

<name>end</name>

<required>true</required>

</attribute>

<variable>

<name-given>value</name-given>

<variable-class>Integer</variable-class>

<declare>true</declare>

<scope>AT_BEGIN</scope>

</variable>

</tag>

 

If you look at the attribute tag, you will see that the attribute start is not required, but the attribute end is. This was just a case of design choice for the class. It makes sense to default to 0 if no start point is specified, but since there are (apparently) an infinite number of prime numbers, it is a good idea to enforce an end point. You will remember that if the JSP page author does not conform to this constraint when writing the JSP page, and supply at least an end value, the container will throw an exception back to the web browser.

After the attribute tag, there is a new tag that you are unfamiliar with, which is the <variable> tag, with its sub-tags <name-given>, <variable-class> and <declare>. These, as you might expect, tell the JSP container about the variable which is going to be added to the page context by your tag. Since this variable is an object which is not a JavaBean, there is no automatic mechanism for telling the container what type it is, so you need to do it with this tag. The meaning of these tags is listed at the end of this chapter, but let’s look at some of them now.

The <name-given> tag tells the container what name to give the variable that it uses. You could use another tag, <name-from-attribute> in its place, which would allow you to specify the name of an attribute whose translation time value will contain the name of your variable. This way you could make the name dependent on some other translation time value.

The <variable-class> tag tells the container what type to make the variable, and the class name specified must be in the classpath at translation time, which is when your JSP file is compiled for the first time on the server. Mostly you will use the standard types found in java.lang anyway.

The <declare> tag tells the container whether or not this variable is declared. This appears to be intended for future JSP development. For now you will always want it to be declared which is the default value.

The <scope> value tells the container at what point your scripting variable should be available to the JSP author. Possible values are NESTED, which makes the variable available only between the start and end tag in the JSP page, AT_BEGIN which makes it available between the start tag and the end of the page, or AT_END which makes it available after the end tag until the end of the page.

At time of writing, there seems to be a problem with the current implementation of Tomcat (4.0.4 ) reading the <attribute> tag. For this reason you might have to use the more advanced and powerful technique of the TagExtraInfo class. This will entail replacing the entire <variable> tag, with a tag called <teiclass> or tag extra info class tag. The details of this are explained later in this chapter.

Tags within Tags

Tags can also be nested within other tags which gives us a powerful structuring facility and also the ability to create conditional output, by having child tags which are only evaluated if certain conditions apply within their parent tag. The JSTL, Java Standard Tag Libraries, make full use of this, but let’s have a look at the highlights of how we might create such a tag ourselves.

Now that you are getting more comfortable with the tag extension API in JSP we only need look the highlights of the next tag. Imagine that you are using the prime number generation tag and you want to check each prime number in turn to see if it is a “Fermat” prime number, and display that to the browser. A Fermat number is a number which can be expressed as “2 to the power 2 to the power n plus 1”, where n is an integer. If your eyes glaze over at mathematical descriptions, don’t worry. Suffice it to say that some prime numbers are Fermat prime numbers, and some aren’t. In fact, the numbers 3, 5, 17, 257, 65537 are the only Fermat prime numbers known.

Since the Fermat prime checker tag is going to have the job of checking each number generated by the prime number tag, it obviously doesn’t make any sense for a fermatprimetag to exist outside of a primenumtag. That means it needs to be defined as a child tag which will prevent users of the tag library containing this tag incorrectly and means that you can make certain assumptions. These will be illustrated in the code. Here is the tag class:

public class FermatPrimeTag extends BodyTagSupport {

public int doStartTag() throws JspException {

PrimeNumTag parentTag =(PrimeNumTag)findAncestorWithClass(this,

PrimeNumTag.class);

if (parentTag == null) {

throw new JspException("Tag should be nested in

\"primenumtag\" Tag");

}

return EVAL_BODY_TAG;

}

public int doAfterBody() throws JspException {

 

Integer prime =(Integer)pageContext.getAttribute("value");

String s = bodyContent.getString();

try

{

if (PrimeUtilities.isFermatPrime(prime.intValue())) {

bodyContent.clearBody();

bodyContent.println(s + " (is a Fermat Prime Number) ");

}

bodyContent.writeOut(bodyContent.getEnclosingWriter());

} catch(IOException ioe) {

throw new JspException(ioe.getMessage());

}

return SKIP_BODY;

}

}

 

Notice the findAncestorWithClass() method. This traverses the hierarchy of tags, finding the parents recursively, until one is found which matches the class you are looking for. The way this method works is by calling the getParent() method recursively. This is another bit of tag management that we inherit from the BodyTagSupport class. If you don’t inherit from this class, you will need to implement the setParent() and getParent() methods yourself. The former is called by the container and will pass you a reference to the parent tag. You would have to store this in a handle for retrieval by the getParent() method.

There might be cases where it is necessary to do all of this by yourself, but they will probably be pretty few and far between. By now you should be convinced of the wisdom of creating your own tags via inheritance from BodyTagSupport.

If the findAncestorWithClass() method does not find a parent tag of the correct type, it will throw an exception that will appear on the client’s browser window. This will tell the user of the JSP library that they have used the tag incorrectly.

The other important thing to notice with this tag is that it is calling the getAttribute() method on the pageContext object to get the value of an attribute called value. This is the attribute that is added to the page context by the parent tag, primenumtag. We have access to this because our child tag, fermatprimetag and our parent tag primenumtag are on the same page, and therefore share the same pageContext object. Remember though, that you still need to clear the body content and get the enclosing writer. What you are actually doing here is replacing the existing prime number with a string displaying the prime number and a message saying “.. is a Fermat Prime Number.”

This tag is much simpler than its parent because it doesn’t need to define any of the looping mechanism. This tag will be treated as a brand new tag each time the parent tag iterates, so the doAfterBody() and other methods will be called repetitively.

The Tag Classes

Knowing whether it is better to use an interface or a class to implement your tag handler can be tricky. It might help to look at a diagram of the classes and interfaces you have at your disposal.

The following table describes some of the useful interfaces in the tag handler API:

Interface

Description

When to use

Tag

The basic Tag Handler from which all other classes and interfaces must inherit

Use this if you want specific behavior for every one of its methods, or your tag handler needs to extend another class. Otherwise, use it indirectly via one of the classes that implements it, like TagSupport..

Iteration Tag

Defines the looping mechanism for a tag by adding the doAfterBody() method to the Tag interface.

Use this if you would otherwise have used the Tag interface but require your tag to be iterative. Otherwise use it via one of the classes that implements it like BodyTagSupport.

BodyTag

Defines the mechanism for handling the body content of a tag, by adding the setBodyContent() and doInitBody() methods to the Tag interface.

 

Use this if you would otherwise have used the Tag interface but require your tag to handle its body content in some way. Otherwise use it via the BodyTagSupport class.

TryCatchFinally

Allows the implementor to create exception handling code that will catch any exception thrown in the handler.

Use this is you require specific behaviour (especially using finally) or if it is imperative that no exceptions be displayed to the client web browser.

 

 

This table describes some of the useful classes in the tag handler API:

 

Class

Description

When to use

TagSupport

Simple class which implements the Tag and IterationTag interfaces and provides useful default behavior for creating basic or iterative tags.

Use this if you want to implement a minimal tag or an iterative tag without doing too much work and you don’t need your handler class to implement any other interfaces.

BodyTagSupport

Simple class which extends the TagSupport class and defines behavior for handling the body content by implementing the BodyTag interface.

Use this if you want to implement a tag or an iterative tag that manipulates its body content, and you don’t need your tag handler to implement any other interfaces.

Once you have chosen the appropriate class with which to implement your tag handler, you will need to work with the companion classes in the API.

This table describes the two companion classes most often needed by tag handlers.

 

Class

Description

When to use

BodyContent

Class which encapsulates the body content of a tag and allows you to manipulate it.

You will need to use this class whenever you are manipulating the body content of your tag.

PageContext

Class which allows storage and access of attributes in the JSP page. The instance of this class you have access to in your tag handler is the same one that can be accessed via the JSP page.

Use this class to make attributes and beans available to the JSP page.

Some Advanced Tag Features

Using TagExtraInfo classes

Sometimes, the variables that you want to make available from your tag for the JSP page author are a little bit too complicated to define in a static TLD file. The names of the variables might be dependent on some set of conditions, or you might want to validate the JSP page. For instance, you might want to make sure that the JSP author hasn’t specified incorrect attributes or nested a child tag in the wrong parent tag. For this kind of functionality, the <variable> tag in the tag library descriptor file is no longer adequate.

In the primenumbertag above, instead of having a <variable> tag, we could have had the following:

<teiclass>cx2.tags.PrimeNumTagExtraInfo</teiclass>

 

This tag tells the container that you have an entirely separate class to describe your attributes for you.

Actually this isn’t as bad as it sounds, the class you need is very simple and you generally don’t need to override more than one method. In the case of the PrimeNumTag class, you would have named the class called PrimeNumTagExtraInfo, to follow the convention, and implement it like this:

package cx2.tags;

import javax.servlet.jsp.tagext.TagExtraInfo;

import javax.servlet.jsp.tagext.TagData;

import javax.servlet.jsp.tagext.VariableInfo;

 

public class PrimeNumTagExtraInfo extends TagExtraInfo {

public VariableInfo[] getVariableInfo(TagData data) {

VariableInfo[] vi = new VariableInfo[1];

vi[0] = new VariableInfo("value",

"Integer",

true,

VariableInfo.AT_BEGIN);

return vi;

}

}

 

Notice that the class extends the class TagExtraInfo, found in the javax.servlet.jsp.tagext package. The getVariableInfo() method creates an array of VariableInfo objects describing the name, type, whether or not it should be declared, and its scope. These correspond with the similarly named elements of the <variable> tag. The array of VariableInfo objects is only of size 1 because there is only one variable.

The TagData object is used as a parameter to the getVariableInfo() method, but this has been designed with future development in mind. Some of the functionality it is intended to support is not yet available in JSP.

Tag Library Descriptors Revisited

If you have another look at the tag library descriptor we used for the tags above, you will find that you are familiar with the first two lines after reading the chapter on XML in this book, The <taglib> tag is self explanatory, defining the start and end of the tag library. Typically you would have one of these per TLD descriptor. It can have several sub elements:

tlibversion - defines a version number which can help the programmer apply some validation at translation time by making it possible to read the version no.

jspversion - defines a version number which helps the container determine which version of JSP is being used, so that if necessary it can turn off more advanced features or perform any special operations that need to be performed for backward compatibility.

shortname - defines an easy-to-use name which a GUI based builder tool might use as the preferred prefix to any generated tags or directives..

uri - defines a unique identifier for the tag library. Typically it is useful to define it in the form of an HTTP URL like the one in the example in this chapter, but it doesn’t have to resolve to a web page, as long as it is unique.

displayname – defines a human readable name that would be useful to display on the toolbar of of a GUI based authoring tool.

small-icon - defines an optional image file, suitable for a builder tool to display on a tool bar.

large-icon - defines an optional image file, suitable for a builder tool to display on a menu panel.

description - defines a description of the tag library which a GUI based build tool might use to describe this library on a tool bar or menu.

validator - defines an optional class that can be used to validate the tag by checking through its parameters and even other tags on the same JSP page, to make sure that the JSP author has used it correctly.

listener - defines an optional class that implements a listener class for the servlet created from the JSP page. The class must implement one of the servlet listener interfaces, e.g. javax.servlet.http.HttpSessionListener.

A <taglib> tag must have at least one <tag> subelement defined. This makes sense, you wouldn’t want to have a tag library with no tags. It wouldn’t be very useful.

The tag element itself has several subelements, some of which you have already come across earlier in this chapter:

name - defines a unique name for this tag, or action.

tagclass - defines the fully qualified name of the class which implements the tag handler interface javax.servlet.jsp.tagext.Tag.

teiclass – defines an optional extra info class, which contains information for validation of the variables for this tag. Must implement the interface javax.servlet.jsp.tagext.TagExtraInfo.

bodycontent – The type of content to be found in the body of the tag, valid values are empty, JSP and tagdependant. If tagdependant is specified, then content will be passed uninterpreted to the tag handler. This would be useful for embedding other languages, e.g. SQL queries.

displayname – A short, human-readable name that can be displayed by a GUI based authoring tool.

small-icon - defines an image file, suitable for a builder tool to display on a tool bar.

large-icon - defines an image file, suitable for a builder tool to display on a menu panel.

description - defines more, optional tag specific information about the tag and how it may be used by other tags. It does this by providing a class which can apply constraints. The class must be a sub-class of the one that implements the tag.

example - defines an optional example of how to use the tag, suitable for a builder tool to display on a help menu.

The last two subelements of the <tag> tag, have subelements themselves.

variable – defines information for a scripting variable defined on this tag. Can be substituted by using the teiclass tag, and providing a tag extra info class.

The subelements of variable are:

name-given – defines the variable name as a constant

name-from-attribute – defines the name of an attribute which will give the name of the variable at translation time.

variable-class – defines the type of the variable. The default is java.lang.String.

declare – whether the variable is declared or not. True is the default.

scope – the scope of the variable. NESTED, AT_BEGIN, or AT_END.

attribute - defines information for an attribute used by this custom tag.

The subelements of attribute are:

name – defines the attribute’s name.

required – defines whether or not the attribute is optional.

rtexprevalue – defines whether or not the attribute may be passed as an expression which can be calculated at run time.

type – defines the type of the attributes. String is default and the most useful.

description – an optional description of the attribute.

Deploying Tag Libraries

Deploying tag libraries takes a fair bit of administration but like any administrative task, the more you understand requirements of each application, the easier it becomes. In the case of tags, there is more than one way to deploy a tag library and you will ultimately have to make the decision as to which is the most appropriate for your application. This would apply whether you were deploying your own tag libraries or ones you were using from a third party vendor.

You have already seen that a tag library descriptor file needs to be created for each tag library, and obviously you want to put that somewhere where the server can find it. You also need to put the Java classes somewhere that the container can find them.

One easy way to do this is as follows:

Put your type library descriptor file, or .tld file on the server in the WEB-INF directory, under your web application directory. e.g. ../webroot/tijtags/WEB-INF, using the path character most suitable for your operating system.

Specify the location of the .tld file in your JSP page using the taglib directive, e.g. <%@ taglib uri="/WEB-INF/tijtags.tld" prefix="tijtags" %>. The uri attribute here indicates the relative path of the tag library descriptor file.

Put your JSP files where you normally would, say in the jsp directory.

Place your classes in the classes directory of your WEB-INF directory, e.g. ../webroot/tijtags/WEB-INF/classes/ making sure that you use subdirectories for the package names, as you would for any other Java application.