Introduction Jakarta-Tomcat is the official reference implementation of the Java Servlet 2.2 and Java Server Pages technologies. Developed under the Apache license in an open and participatory environment, it is intended to be a collaboration of the best-of-breed developers from around the world. Jakarta-Tomcat is a servlet container. Servlets are memory-resident Java programs, running inside a servlet container. Installing Jakarta-Tomcat Jakarta-Tomcat can be installed by downloading jakarta-tomcat-3.2.1.zip from the jakarta.apache.org site. This zip file is approximately 3 MB large. On unzipping this file, it will create a subdirectory called jakarta-tomcat-3.2.1, all the files will be installed in this subdirectory. On our machine, we have extracted this file to the root i.e. C:\. This assumes that you have downloaded version 3.2.1. By the time this you read this, version 4 will be out and all subdirectory names will change accordingly. However, all the programs should work in the same way. Change to the bin directory in c:\jakarta-tomcat-3.2.1 and run the batch
file 'startup'. This will open up a window and on your dos screen you
will see the following statements: If you donot see the output as shown above, edit the startup.bat file
and add these two lines at the beginning. In this chapter, we will build some simple servlets. This will help demonstrate the power and flexibility of the Java platform. Servlets are at the frontline of Java web application development. They provide an easy way for server side code to communicate with web-based clients. In simple words, they are programs that run on a Web server and build Web pages. startup is a batch file that will start the Java Web server. Minimize the screen and activate another DOS box. Change to the jakarta-tomcat-3.2.1 directory and then go to webapps. From this directory go to examples, then web-inf and then go to classes. C:\jakarta-tomcat-3.2.1> cd webapps\examples\web-inf\classes All the programs that you key in will be stored in this subdirectory. C:\jakarta-tomcat-3.2.1\webapps\ examples\web-inf\classes > In this subdirectory, create a file called zzz.java containing the following
3 lines. On compiling, javac gives an error because the extended class is javax.servlet.http.HttpServlet and not HttpServlet. C:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes>javac zzz.java zzz.java:1: Superclass HttpServlet of class zzz not found. Since we don't want the name to be lengthy, we will use the import statement. Doing so will get rid of the error. If you do get an error, make sure the CLASSPATH variable has c:\jakarta-tomcat-3.2.1\lib\servlet.jar as one of the directory paths. The code for applet.class is in some jar file and by default javac looks for it. Unfortunately, it could not find the code of javax.servlet.http.HtttpServlet and thus you got an error. The code for javax.servlet.http.HttpServlet is in servlet.jar which is in the lib subdirectory. A jar file is collection of .class files. Once you set the classpath correctly, javac looks at the jar file specified to see if it contains the required .class file. ClASSPATH is an environmental variable and it may have already been set to a certain path. To avoid overwritting the original information, we say %CLASSPATH%. Our modifications will now be added to the begin of the list. We have given the CLASSPATH command as C:\jakarta-tomcat-3.2.1\webapps\examples\WEB-INF\ zzz.java Start any browser, it can be Internet Explorer or Netscape, and give the address as http://127.0.0.1:8080. First let's understand what this means. You are probably aware that every phone has a unique phone number. Similarly, every computer on the Net has a unique number. We can't just call it a number, so we call it an IP address. For example, the Microsoft site, like all other sites, has a unique IP address. If each computer on the Net were not identifiable by an IP address then we would not be able to connect to them. Each machine also has a common IP address 127.0.0.1 and can be represented as 'localhost'. You use this number to refer to your machine. Both 127.0.0.1 and localhost are the same. Since the address we're typing in is 127.0.0.1, it means that both the client and the server are running on the same machine. Now, you may need to have ftp, email and http servers. That however, doesn't mean that you need a separate machine for each of them. Each packet on the internet has a number. For example, 25 is for email, 80 is for http. So, instead of separate machines, we have separate ports. These packets can be intercepted at the respective ports and passed on to the relevant server. Every server runs on a particular port. Just as the http server runs on port 80 and a smtp server runs on port 21, the Java Web server runs on port 8080. In case you want to change the port number or the original settings of the server, you can do so by giving various options to startup. Since we are not interested in doing this, we will simply use 8080. After giving address:port, give /examples/servlet/ followed by the name
of the class file, which is zzz. When you write /examples/servlet/, it
is automatically converted to c:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes
which is where Java will look for the zzz class file. This is known as
url mapping. In effect, you are mapping from a logical url to a physical
directory. So when you say /examples/servlet/, the Java web server takes the /examples/servlet/ and maps it to a path on the hard disk. Then it picks up the file zzz.class from the directory. Unfortunately, you will see a screen with an error 'saying HTTP method GET is not supported by this URL. If you look at the server DOS box, it displays a similar error. This obviously implies that we have done something wrong or a lot more must be added to get the servlet to work. Add a function named doGet to zzz.java. This function receives two objects as parameters, req and res. req looks like HttpServerRequest and res looks like HttpServerResponse. They are to be found in HttpServlet. zzz.java Compile this program and you will get no error. By merely adding this one line, you will find that the error vanishes. Now we don't get an error because the server can now call the doGet function. The server looks for a doGet function in the class file and since there was no such function, we got the error. When zzz is loaded in the browser, it displays a blank screen (no error). In Internet Explorer, click on View and select Source, you will see html tags. View - Source We obviously didn't give these tags in our program. Hence it is assumed that the Java Web server created these tags and gave it to the browser. A small piece of advice: Create a new servlet each time i.e. give a new file/class otherwise the browser takes it from the cache. Since the servlet is present in memory, even when you make changes in your program file there is no effect as the program output is picked up from the cache. Alternatively, you can use zzz for all your programs, but then after every change you will have to stop the server and then restart it. We will stick to zzz. The Web Server is another name for the File Server. The web server is expected to send an html file over. Create an html file z.html in c:\jakarta-tomcat-3.2.1\webapps\root and type the following. c:\jakarta-tomcat-3.2.1\webapps\root>edit z.html Now view it in the browser by typing http://127.0.0.1:8080/z.html Let's look at the next program and see what it displays. In the earlier chapters, we learnt about System.out.println. Here we are giving <b>Bye as a parameter to this function. When you run the servlet,(remember to stop the server and then start it again) you will see a blank screen as before. View Source shows the same html file. But if you look at the server dos box it will show you <b>Bye. System.out.println prints in the dos box. This proves that the function doGet is indeed being called by the web server. We need to send an html file accross. Unfortunately, println displays its output in the dos box. So, let's see how the servlet gives the web server an html file so that it can send it to the browser. zzz.java C:\jakarta-tomcat-3.2.1\webapps\ WEB-INF \examples\web-inf\classes>javac
zzz.java This program creates a variable 'out' which looks like PrintWriter. It also uses res, a parameter passed to doGet, which looks like HttpServletResponse. res has a member named getWriter which returns an object that looks like PrintWriter. PrintWriter has a member named println and hence the next line says out.println. This function is similar to System.out.println in terms of syntax. With System.out.println, 'out' was a member of System, but here out is a member of the PrintWriter class. The only problem here is that the name is not PrintWriter and hence we get all these errors. Printwriter is not part of javax.servlet.http.HttpServlet but of java.io.
Its full name is java.io.PrintWriter. So we add the import statement as
shown below. C:\jakarta-tomcat-3.2.1\ webapps \examples\web-inf\classes>javac zzz.java zzz.java:7: Exception java.io.IOException must be caught, or it must
be declared in the throws clause of this method. We now have to use try and catch in order to handle the exception. zzz.java Note that we have given "<b>Bye" to out.println. This is because <b> in html means bold. We want Bye to be displayed in bold letters and the <b> tag will allows us to do that. We are not catching the IOException exception thrown by getWriter but instead are catching Exception. We are using Exception because it catches all the exceptions that can ever occur. There is a possibility that getWriter may throw two different exceptions; in that case you will have to catch both of them with separate try-catchs. The right way to catch exceptions is to have a separate try and catch for every function that throws an exception. We are however, using a single try block for all the statements so that we don't have to worry about the exceptions that occur. Now, run the program from the browser. Great! Finally, you get to see Bye on the screen! Click on View source and it will give you <b> and Bye. View Source Let's say we have a file a.html. When the web server sends a file over, it actually first sends some data and then the html file. This initial data is called a header. The first line of the header in most cases is HTTP/1.0 200 OK. HTTP stands for the hyper text transfer protocol. A protocol is a set of rules. The http protocol specifies how a browser communicates with a web server. It decides on issues like port nos to be used for communication, the error codes and what they mean and so forth. All web servers follow the http protocol. zzz.java View Source A header is always sent along with an html file. The browser knows about the type of the document being received by looking at the header. Thus, if the browser finds that the header says text/plain then it will display the text as it is. If it sees that the header has text/html then it will display the text in the html format. Since we did not set the header for Content-Type earlier, it was assumed to be text/html. In this program, we have res and req. To receive something from the client we use req and to send something to the browser we use res. res has functions that lets you send something to the web server. That content then has to be passed to the browser. res, that looks like HttpServletResponse, has a function named ContentType which by default is set to text/html. Here we change it to text/plain. So we have added res.SetContentType("text/plain") in the above program. The actual header that is generated looks like this. Content-Type: text/plain A header is sent before the actual data and it consists of words which
end in a colon. These words describe the rest of the html file being sent
across. Text/plain is called a mime type. Content-Type is a header which
tells the browser the nature of the file being sent across. If it said
image/gif, then the browser would know that it is receiving a picture
file which has a gif file format. You can change it back to text/html if you like. But it is not necessary as the default is text/html. zzz.java These programs that we have been writing so far are called Servlets.
Why so? Well because they are derived from HttpServlet. A servlet is a
class which is derived from HttpServlet. To use the throws clause, use the word throws followed by the exception. The function doGet, in the following example, says throws IOexception. Hence, you don't have to put a 'try-catch' within the code for the functions throwing exceptions. zzz.java Here, in out.println we have included the <HTML> </HTML> tag. Normally, html code is written within the html tag. Since html is forgiving, it doesn't matter if you choose not to include these tags. That's the reason we got away with it in our earlier program. It is good programming practice to close a file and if you don't, it is closed by default. out.close closes the newly generated file. If you run this from the browser, it will dispaly 'hi' in the browser window. View Source When we want to dynamically generate html files, we use programs written in C, Perl, Java etc. Perl is an interpreted language and so it is very slow. Java servlets stay in memory and are thus quite fast. Perl as a program is called each time the program needs to be run. Perl runs, generates a file and then quits. If a Perl program is required to connect to a database, maybe to retrieve data and then disconnect, imagine the amount of time involved to complete the transaction!. This CGI program is great if it is for a few connections but not for a million connections. Servlets are not removed from memory. To prove this, all that we do in the next program is, display the value of i, which is a variable created outside a function. zzz.java zzz.java In Internet Explorer, the functions returned the following values: To understand all these headers, you have to go to the relevant RFC (Request For Comments). RFC's are internet standards. The RFC will tell you what each one of the headers is all about. There are thousands of RFCs on the net so you will have to find and read the RFC about the HTTP protocol. Thus, the servlet knows about the server and user's data from the headers sent in by the server. Type in the next program as given and in the browser address bar type the url as: http://127.0.0.1:8080/examples/servlet/zzz?pqr=abcd zzz.java getParameter is a function in HttpServletRequest. To this function, a
parameter pqr is given. req.getParameter removes the = sign and assigns
the value of pqr ie abcd to aa. In the url, we've given pqr=abcd, so the
string aa becomes abcd. This is how the servlet can figure out what the user has typed. If instead of abcd you had initialized pqr to abcd xyz then aa would return abcd xyz. Once you press enter, the screen will show pqr as abcd, but if you look at the address bar, it will display %20 in the place of a space. At times instead of a '%' sign it may also show a '+' sign. All the reserved characters get converted to ascii but in hexadecimal. The RFC forbids you to use certain characters like spaces so they are encoded as '+' or '%'. Since we can't use reserved characters in a url, they have to be replaced with their hexadecimal equivalent. To sum up , if you use the reserved characters in a URL, then as specified in the RFC, they are converted to hexadecimal characters. Now, if you want to search for some word in a search engine, you will type that word in the text box provided and press enter. The search engine will search its database for that word and provide a list of all the sites that are related to it. This list will then be passed on to a program which will generate an html file. Let's create an html file where you can enter some text in a text box. Remember webapps\root is where you create the file. >edit a.html a.html Form is the tag used for creating a form in html. input type=text specifies that we want to create a text box and the name of that text box is pqr. So, this will dispaly a simple text box. Type the following in the address bar http://127.0.0.1:8080/a.html Now type 'abcd' in the text box and press enter or click on search. Take note of the url in the address bar. It says http://127.0.0.1:8080/aa.html?pqr=abcd Earlier, we typed a similar url ourselves in the address bar. Now we are generating it through the html file. Ignore the error that you see in the browser window. Now edit your a.html file to match the following a.html Here, we have an additional text box called aa and a button called submit. http://127.0.0.1:8080/aa.html?pqr=abcd&aa=efgh Thus you can have many name value pairs in the url, each seperated by &, after the '?'. In all these programs, we have been seeing the parameters in the url. We now have a servlet which takes these values and displays them in the browser. So, lets rewrite the servlet and compile it and then create a small html file that does this for us. zzz.java If you are using many values in the address line, use getParameter as before. As long as you know the name of the textbox, you can get the related value without a problem. b.html Save the html file in c:\jakarta-tomcat-3.2.1\webapps\root. In the browser
enter the url as In the text boxes, type in 'hi' in the first one and 'bye' in the second.
When we click on the button labeled Click, a new screen similar to the
one seen before emerges in the window. Our program, zzz, gets called.
This is because we specified it in action. Also, we have specified method=get.
When we use get, whatever you type in the text box, along with its name,
becomes a part of the url. Give the method as post and you will see that
the fields are not sent as a part of the url. They are sent separately.
We will explain get and post later. http://127.0.0.1:8080/examples/servlet/zzz?pqr=hi&xyz=bye zzz generates an html file depending on what we type in the text boxes. req.getParameter picks up the values for the names specified. pqr is hi xyz bye What we have been trying to explain is called CGI Programming. CGI stands for Common Gateway Interface. It specifies how data is transferred from the browser to the web server and vice-versa. When we visit a site, it may ask us to fill up a form. The data we key in is sent to the server as name value pairs. The name is the name of the text box and the value is what we key in the text boxes. The next program is a little lengthier. zzz.java This program uses a function named System.getProperties. System is a class and it is something that Java gives us for free. In our earlier programs, we worked with an object named out. Here, we have getProperties which returns an object that looks like Properties. It is similar to the 'out' object. This value is stored in a variable called props that looks like Properties. The Java Web Server has a large number of properties. We intend to list all of them. A property is a name and an associated value.When you say aa=bb, aa is the name of the property, bb its value.You can have hundreds of such properties. As the name of the property changes, the value changes too. What you need to do for one, you need to do for all. Since we want to access all properties and are not aware of the number, we use an Enumeration. An Enumeration represents an array, enumeration means more than one of the same thing. Ten people become an enumeration. What can be done with one can be repeated for all ten. So, an enumeration is like a for statement where you simply repeat. We need an object that looks like an Enumeration, props.propertyNames() returns a similar object. This object is stored in a variable called enprop. enprop denotes all the property names. Many functions return an Enumeration. Any Enumeration has a member function named hasMoreElements. Assuming there are ten property names, the while loop will go on ten times. The function will return true when there are more elements, otherwise it will return false. nextElement will give the next element. The variable named key, which looks like a string, will be equal to the return value of enprop.nextElement. Note that nextElement may return a string or a number but key is a string. We have given (String) so that no matter what nextElement returns it is cast to a String and given to key. Remove (String) and you will get an error saying you need to cast it to a String. But what does enprop.nextElement return? It returns the property name. We can retrieve the property name only if the property exists. Hence the presence of the property is checked first. If the first property is named aaa, which has the value 'vijaymukhi', then enprops.nextElement will return aaa which will be stored in key and getproperty("aaa") will be its value which is vijaymukhi. So the Enumeration will give all the names and getProperty will get its value. You may not want to get into all these details. Alternatively, props has a member called 'list' to which you can give the variable 'out'. This will give you a similar listing. Thus in Java, whenever there is a name=value pair and there are a number of such pairs, this method can be used to access all of them. Enter the url as http://127.0.0.1:8080/servlet/zzz and it will display an output which is too large to be dispalyed. In the following program we have used a function called getParameterNames
which gives us the names of the parameters. zzz.java The req.getParameterNames returns a string. The value is not a single value but an array of strings. Hence we have used val.length to find the number of members in the array. The for loop allows us to display them.
In the same way, we can get the header names and cookies. Thus, even though you don't know about cookies as yet you are able to do this.Cookies have been dealt with in one of our chapters further on. zzz.java Output Since we don't have any cookies at present, we don't see anything. If you have understood the earlier example then this one will be a cake walk. It is basically for accessing multiple things that are similar. We also see the list of headers we are being sent by the browser. In servlets, you can do anything you want. You can pick up an html file from your hard disk and send it over or go to another site on the net, pick up an html file from there and send it across. Let's see how the second method works. zzz.java This program retrieves a page from the net and sends it to the browser. Let's understand the mechanics of it. A url identifies a resource. So 'new URL' takes a string, a site address or a url and returns an object that looks like URL. Here we have given our site address, so make sure that you are connected to the Internet. The object that looks like URL has a function named openConnection. 'u' is just the form of the url. It is broken up into protocol, portno, address parameter and other things like that. It actually uses the TCP protocol to physically connect to the webserver and returns an object that stands for the connection. That is why c is not a URL, but an object that looks like a URLConnection. Now we need to read and write to this connection. The getInputStream function of URLConnection returns an object that looks like InputStream from where you can read stuff. The only problem is that the InputStream is too low level. To read data from this stream, an object that looks like DataInputStream is created. The developers of Java could have stopped at InputStream but they want us to use DataInputStream. You must be asking yourself useless questions like 'Why'? Don't torture yourself with the unnecessary! DataInputStream has functions like readLine, which returns data one line at a time. When there are no more lines to return, it returns a null. Once some data is received, out.println is used to display it in the browser and the stream is then closed. One of the functions throws an exception and we catch the exception so that the program ends normally. An applet is a Java program that runs within your browser. The browser has an applet engine which executes the code coming in from across the net. A servlet is also a Java program but it executes on the server. Unlike the browser, the server doesn't execute a servlet but instead calls a program named the Servlet Engine. This program executes the Java code. Just as an applet must be derived from Applet, a servlet must be derived from Servlet. The whole idea behind servlets is that the Servlet Engine executes Java code, which creates the html file and gives it to the web server. The web server then sends it across to the browser. zzz.java C:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes>javac zzz.java zzz.java:1: Superclass Servlet of class zzz not found. In this program, zzz extends Servlet. HttpServlet, which we used earlier,
too is derived from Servlet. Had we derived from Servlet in the earlier
programs, then our programming would have been more tedious. So, to demonstrate
and appreciate how HttpServlet makes life easier for us, we will extend
Servlet. We know that HttpServlet is derived from Servlet because we looked
at the source code. If you have downloaded Jakarta-Tomcat with the source
code option then you can edit the file HttpServlet.java and see for yourself. The compiler comes back with an error saying Servlet not found. We got an error because we have not specified the full name. To remove the error, give an import statement because the full name is javax.servlet.Servlet. We know that this is the full form becuase we looked at an existing example. Also be sure to set the classpath as before. zzz.java C:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes>javac zzz.java zzz.java:2: Can't subclass interfaces: interface javax.servlet.Servlet Giving the full name does not seem enough! Javac comes back with an error because it knows javax.servlet.Servlet to be an interface. An interface and a class look the same. The help files will tell you that Servlet is an interface. You cannot distinguish between a interface and a class using just the name. zzz.java To use an interface, we write 'implements Servlet'. We've explained earlier
that an interface is a collection of function prototypes. Unfortunately,
when we say implements Servlet, we get 5 errors. Runnable had only one
function called run, this one has 5 functions. When you say implements
Servlet, it guarantees that it looks like a Servlet and that it defines
all the five functions. Because the Java Web server expects a class derived
from Servlet, it expects your program to have those same five functions.
zzz.java:2: class zzz must be declared abstract. It does not define void
destroy() from interface javax.servlet.Servlet. We now need to create these five functions in zzz and the only statement included in these functions is System.out.println. This is what the next program shows. zzz.java When a function returns void it means you don't need to give a return value. But if it returns a String, then either you must return a meaningful string or an empty string (which is two double quotes). When the function returns a ServletConfig then we must return a ServletConfig. Let's check that out! say return 0 like we have done in the following program. zzz.java C:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes>javac zzz.java So, we must return a ServletConfig. In order to do that 'a' is made a
public variable, which looks like ServletConfig and is returned from getServletConfig.
Now you're probably wondering why we've made it public and haven't put
it in the function itself. Well, in that case you will have to use new.
Let's see what happens. Make the changes in the program as shown below. C:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes>javac zzz.java You will now get an error saying you can't instantiate from an interface. There is no other way out. We have to make it public. So zip back to the original program, the one where 'a' is public, and run it. Javac will not complain. Move to the browser and check if the servlet works over there. Well, it does work! It shows a valid blank screen. But to actually confirm that the servlet has worked, switch to the server dos box. Out of the 5 functions, only two of them get called, init and service. init gets called once and service gets called each time refresh is clicked. So, click on refresh and check the dos box. Now let's do something smart. It may be too much to ask but we believe
in making the impossible possible! Put all the code that was in doGet
into service within the try and the catch. Everything works as advertised.
As before, init and service are called. Service takes the same parameters as doGet. So we follow the same logic, that is, create a PrintWriter and write to the PrintWriter. Earlier, we derived from HttpServlet. HttpServlet has the same five functions
that we have in zzz, so it's possible to replace HttpServlet with zzz.
In either case, the Java Web server calls init only once. If you have
an init in your program then that's the one that's called, else, the init
in HttpServlet is called. Both init's don't do anything right now and
it is upto you to decide if you want init to do something. You can put
code that is to be executed once in init. We implement from Servlet because the Java Web server insists that we do so. If you remove implements Servlet from zzz it will give you tons of errors. This shows that HttpServlet does implement servlet. You must have the five functions, but the order of functions is not important. The Java developers realized that programmers would not like the idea of writing these functions each time, so they created a class called GenericServlet containing these functions. In the next program, zzz extends GenericServlet. On saying javac, it gives an error on service. zzz.java C:\jakarta-tomcat-3.2.1\webapps\examples\web-inf\classes>javac zzz.java zzz.java:3: class zzz must be declared abstract. It does not define void
service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) from
class javax.servlet.GenericServlet. To understand this error let's look at a part of the GenericServlet file. We have printed a part of this file relevant to our discussion. GenericServlet.java You can search for this file with an extension of .java or .html in the jakart-tomcat subdirectory It contains the service function, but we will have to write our own service function because the class GenericServlet is abstract. We will explain what we mean by that with the help of an example. yyy.java xxx.java Now let's create ppp.java and extend from yyy. ppp.java Run this and you get an error similair to the one we got when we tried to extend from GenericServlet. That means when a function is declared as abstract in the class we are extending from, we cannot directly use it. Instead we have to write our own function i.e we have to override it. So make the following changes in ppp.java The class yyy implements an interface xxx. xxx has 2 function prototypes. yyy decides to have the code for only one function aaa and not bbb. Now if we compile yyy we will get an error. Thus the only way to remove the errors is by making bbb abstract and also the class abstract. Now if anybody wants to extend from the class yyy, he has to write the code of bbb. ppp.java Since bbb is abstract, you cannot call it directly. We are overriding bbb by giving our own function. Now compile it and you will not get any errors. Similarly, for our previous program we have to override service. So in the next program we have added service and init. zzz.java Any servlet can be extended from GenericServlet. Even HttpServlet is
derived from GenericServlet. The service function in HttpServlet checks
for the method and accordingly calls doGet and DoPost. It also contains
init. GenericServlet makes writing servlets much easier. a.htm zzz.java Remember, the html file must be in c:\jakarta-tomcat-3.2.1\webapps\root. The only difference between 'get' and 'post' is that in get, the query string is passed as a header value. So a header called Query_String is created which contains all that we have keyed in. If you say method=post then the data is sent separately. It is not a part of the header. There is a finite amount of data that can be given to the environmental variable, so if you're not going to be exceeding that, use 'get'. When you want to send a large chunk of data, then use 'post'. Other methods are available too, but they are not used as frequently. Conclusion You have seen how the Java servlet is both simple and powerful. It allows
us to extend the functionality of any web server with just a few lines
of code. |