Servlets are memory-resident
Java programs, running inside a servlet container. Servlets are at the
frontline of Java web application development as 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.
Create a subdirectory
mservlet with the folder :
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes.
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes> md mservlet
We will explain the reasons behind it once we see our programs work. Now within this subdirectory create zzz.java with the following code.
…\classes\mservlet>edit zzz.java
zzz.java
public class zzz extends HttpServlet
{
}
On compiling, javac gives an error because the extended class is javax.servlet.http.HttpServlet and not HttpServlet.
\mservlet>javac zzz.java
zzz.java:1: cannot resolve symbol
symbol : class HttpServlet
location: class zzz
public class zzz extends HttpServlet
^
1 error
Since we don't want the name to be lengthy, we will use the import statement. Doing so will eliminate
these errors.
import javax.servlet.http.*;
Inspite of adding the import statement we yet see an error. The code for applet.class and some basic classes is in some jar file and the javac compiler by default looks at these jar files at compile time. A jar file is collection of .class files. However, none of the jar files have the required code for HttpServlet. Since the code of javax.servlet.http.HtttpServlet is untraceable, an error apears. The code for javax.servlet.http.HttpServlet is in j2ee.jar, which is in the lib subdirectory.
The ClASSPATH is an environmental variable and it may have already been set to a certain path. The variable is to be reset for javac to look at the new jar file. Thus to avoid overwriting the original information, we give the CLASSPATH command as
C:\javaprg>set CLASSPATH=C:\Sun\AppServer\lib\j2ee.jar;%CLASSPATH%
zzz.java
package mservlet;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
}
Also, notice the use of package with the subdirectory name, mservlet. Compile the code and we assure you there will be no errors.
Packages
If you had a class called Applet and if another user also called his class Applet, then whose Applet gets called, yours or his? To prevent a name clash, sun invented the concept of a package. They said that all classes created by Sun would start with java. Unfortunately this does nothing for a grouping. We would like all classes that deal with GUI issues to be grouped together. Thus we have one more level i.e. all GUI classes are part of java.awt and applet classes are a part of java.applet. Hence the full name of the class Applet is java.applet.Applet and java.applet is the package name which can be part of the import statement.
However before we see our servlet in action, we have to add a few entries to a file ‘web.xml’ in
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF
Open the file using notepad and add the following entries at the right position. Insert the block after HelloWorldExample. Though the order is of no significance, we suggest that for the moment you do precisely as we advise you to avoid any fallout.
Web.xml
..
..
..
<servlet>
<servlet-name>HelloWorldExample</servlet-name>
<servlet-class>samples.webapps.simple.servlet.HelloWorldExample</servlet-class>
</servlet>
<servlet>
<servlet-name>zzz</servlet-name>
<servlet-class>mservlet.zzz</servlet-class>
</servlet>
<servlet>
<servlet-name>RequestInfoExample</servlet-name>
<servlet-class>samples.webapps.simple.servlet.RequestInfoExample</servlet-class>
</servlet>
..
..
..
<servlet-mapping>
<servlet-name>HelloWorldExample</servlet-name>
<url-pattern>/HelloWorldExample</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>zzz</servlet-name>
<url-pattern>/zzz</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RequestInfoExample</servlet-name>
<url-pattern>/RequestInfoExample/*</url-pattern>
</servlet-mapping>
Start the Java Web server by Clicking on Start-Programs-Sun Microsystem-J2EE 1.4 SDK- Start Default Server. First the DOS box comes up detailing the server progress and then the server window is seen but minimized. Once the server is ready to handle all requests, press a key in the dos box as indicated.
Now start any browser, it can be Internet Explorer or Netscape, and give the site address as
http://127.0.0.1:8080/webapps-simple/zzz
Output in browser window
HTTP Status 405 - HTTP method GET is not supported by this URL type Status report
message HTTP method GET is not supported by this URL
description The specified HTTP method is not allowed for the requested resource (HTTP method GET is not supported by this URL).
Sun-Java
The browser window will show you the following errors. Though this is enough proof of our servlet being accessed, we modify the program a bit to see it actually work. You will have to stop the server and restart it again for every new change in the program.
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
package mservlet;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
}
}
Compile this program and you will get no error. By merely adding this one line, you will find that the error vanishes. There are no errors on the screen because the server detects the doGet function in the program. 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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=windows-1252"></HEAD>
<BODY></BODY></HTML>
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.
Now a bit of explanations of our doings.
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 /webapps-simple/ followed by the name of the class file, which is zzz. The /webapps-simple/, is converted to a certain directory 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.
If you recall, we had made a few entries in web.xml. Lets understand these entries now.
<servlet>
<servlet-name>zzz</servlet-name>
<servlet-class>mservlet.zzz</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>zzz</servlet-name>
<url-pattern>/zzz</url-pattern>
</servlet-mapping>
The url pattern /zzz is /zzz in the url address http://127.0.0.1:8080/webapps-simple/zzz
This pattern is mapped to a servlet named zzz . Further, the servlet zzz has a full name which is mservlet.zzz.
This servlet class is searched for in the directories assigned to the CLASSPATH variable. Once located, the class is loaded in memory. Unfortunately, an error stating ‘HTTP method GET is not supported by this URL’. This obviously implies that we have done something wrong or a lot more must be added to see the servlet work.
However, when the doGet function is inserted, as in , public void doGet(HttpServletRequest req,HttpServletResponse res), the error disappears.
So with this brief understanding on servlets, lets proceed further to explore the possibilities when working with the Java Web Server. The Web Server is another name for the File Server. The web server is expected to send an html file over. So, create a html file z.html in C:\Sun\AppServer\domains\domain1\docroot> and type the following.
C:\Sun\AppServer\domains\domain1\docroot>edit z.html
z.html
Hi
Bye
Now view it in the browser by typing
http://127.0.0.1:8080/z.html
You will see Hi and Bye displayed in the browser window thus proving that the web server sends z.html over to the browser. However, all of this is static. What we need are pages which are dynamically generated.
For this, the web server calls another program that processes the input and creates an html file. This is called CGI Programming. The web server simply sends the generated html file across to the browser.
So lets proceed in this direction. Take the next program and see what it displays.
zzz.java
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
System.out.println("<b>Bye");
}
}
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 log file i.e., C:\Sun\AppServer\domains\domain1\logs\server.log, it will show you <b>Bye.
System.out.println redirects its output to the log file. This proves that the function doGet is indeed being called by the web server.
Our aim is to send an html file accross. So, let's see how the servlet gives the web server an html file which will then be sent to the browser.
zzz.java
package mservlet;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
PrintWriter out;
out = res.getWriter();
out.println("<b>Bye");
}
}
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes\mservlet>javac zzz.java
zzz.java:7: cannot resolve symbol
symbol : class PrintWriter
location: class mservlet.zzz
PrintWriter out;
^
1 error
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.
zzz.java
package mservlet;
import java.io.*;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
PrintWriter out = res.getWriter();
out.println("<b>Bye");
}
}
Here, getWriter throws an IOException. This exception is thrown when it cannot create an object like PrintWriter. The error is shown below. We won't talk about errors any more, instead we'll say that an exception occurred. To handle the exception, we have to catch it.
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes\mservlet>javac zzz.java
zzz.java:8: unreported exception java.io.IOException; must be caught or declared
to be thrown
PrintWriter out = res.getWriter();
^
1 error
We now have to use try and catch in order to handle the exception.
zzz.java
package mservlet;
import java.io.*;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
try
{
PrintWriter out = res.getWriter();
out.println("<b>Bye");
}
catch (Exception e){}
}
}
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.
http://127.0.0.1:8080/webapps-simple/zzz
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
<b>Bye
Let's say we have a file a.html. When the web server sends a file over, it initially sends some data headers and then the html file. 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, thus the http protocol has specifications on 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
package mservlet;
import java.io.*;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
try
{
res.setContentType("text/plain");
PrintWriter out = res.getWriter();
out.println("<b>Bye");
}
catch (Exception e){}
}
}
http://127.0.0.1:8080/webapps-simple/zzz
View Source
<b>Bye
A header is always sent along with any file. The browser recognizes the type of the document being received by looking at its headers. Thus, if the browser receives text/plain, it will display the text as is. However, if it sees the header with text/html then it will display the text in the html format. When the header for Content-Type is not set, it is assumed to be text/html.
The doGet function has two parameters, res and req. The client date is available in req whereas the servlet data that is to be transmitted to the browser is given in res.
res thus has functions that aids in sending data to the web server which ultimately is passed to the browser. res, that looks like HttpServletResponse, has a function named ContentType which by default is set to text/html. In the above program, we have changed it to text/plain, as in res.SetContentType("text/plain"). The actual headers that eventually get generated is :
Content-Type: text/plain
A header is sent before the actual data and it consists of words which end in a colon. These words give information on the file being sent across. Content-Type is a header which notifies the browser on the nature of the file being sent across. Text/plain is called a mime type. If it is image/gif, then the browser would know that it is receiving a picture file which has a gif file format.
Even though, in the program, we did not specify any headers, the java web server sends a large number of them over. We have the option of changing the values of these headers as well as send our own.
Once the browser knows that the text coming in is an html file, it interprets <b> and displays the remaining text in bold. It works perfectly and as advertised, hence the Bye becomes bold.
You can change it back to text/html if you like, though not necessary as the default is text/html.
zzz.java
package mservlet;
import java.io.*;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
try
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<b>Bye");
}
catch (Exception e){}
}
}
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.
An alternative to try-catch is the the throws clause followed by the exception. The function doGet, in the following example is adjuncted with throws IOexception. Hence, you don't have to put a 'try-catch' within the code for the functions throwing exceptions.
zzz.java
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML> <b>hi </HTML>");
out.close();
}
}
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. This servlet will dispaly 'hi' in the browser window.
View Source
<HTML> <b>hi </HTML>
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 however Java servlets once loaded in memory remain residents 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 remain loaded in memory, they are not unloaded. 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
package mservlet;
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
int i = 0;
public void doGet(HttpServletRequest req,HttpServletResponse res) throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
i++;
out.println("<HTML> hi " + i + " </HTML>");
out.close();
}
}
Here we are incrementing i by 1 in the function doGet. The first time that doGet gets called, i will be incremented by 1, and then each time you click on Refresh, it will keep incrementing by 1. This proves that the code is memory-resident. This is one of the reasons why you need to stop your server and then restart it if you want to use the same servlet. Also try using a fresh copy of IE or Netscape and you'll notice that the value of i is retained across browsers. Hence if it is 3 in the first copy, clicking on Refresh in the second will make it 4 and then clicking on Refresh in the first copy makes it 5. Thus, we can see that servlets never get removed from memory.
The web server sent Content-Type as one of the headers to the web browser. Similarly, there are headers that are sent by the web browser to the web server. To figure out what these headers are, HttpServletRequest is given as a parameter to doGet.
zzz.java
package mservlet;
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res) throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML>");
out.println( "Request method : " + req.getMethod()+"<p>");
out.println( "Request URI : "+ req.getRequestURI()+"<p>");
out.println( "Request protocol : "+ req.getProtocol()+"<p>");
out.println( "Servlet path : " + req.getServletPath()+"<p>");
out.println( "Path info : " + req.getPathInfo()+"<p>");
out.println( "Path translated : " + req.getPathTranslated()+"<p>");
out.println( "Query string : " + req.getQueryString()+"<p>");
out.println( "Content length : " + req.getContentLength()+"<p>");
out.println( "Content type : " + req.getContentType()+"<p>");
out.println( "Server name : " + req.getServerName()+"<p>");
out.println( "Server port : " + req.getServerPort()+"<p>");
out.println( "Remote user : " + req.getRemoteUser()+"<p>");
out.println( "Remote address : " + req.getRemoteAddr()+"<p>");
out.println( "Remote host : "+ req.getRemoteHost()+"<p>");
out.println( "Authorization scheme : " + req.getAuthType()+"<p>");
out.println("getCharacterEncoding:" + req.getCharacterEncoding()+"<p>");
out.println("getScheme: " + req.getScheme());
out.println("</HTML>");
out.close();
}
}
In Internet Explorer, the functions returned the following values:
Request method : GET
Request URI : /webapps-simple/zzz
Request protocol : HTTP/1.1
Servlet path : /zzz
Path info : null
Path translated : null
Query string : null
Content length : -1
Content type : null
Server name : 127.0.0.1
Server port : 8080
Remote user : null
Remote address : 127.0.0.1
Remote host : 127.0.0.1
Authorization scheme : null
getCharacterEncoding:null
getScheme: http
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.
In the browser address bar type the url as: http://127.0.0.1:8080/webapps-simple/zzz?pqr=abcd
The output now changes slightly
Request method : GET
Request URI : /webapps-simple/zzz
Request protocol : HTTP/1.1
Servlet path : /zzz
Path info : null
Path translated : null
Query string : pqr=abcd
Content length : -1
Content type : null
Server name : 127.0.0.1
Server port : 8080
Remote user : null
Remote address : 127.0.0.1
Remote host : 127.0.0.1
Authorization scheme : null
getCharacterEncoding:null
getScheme: http
Observe the Query string value. In the earlier case it had a null value whereas now it holds the characters after the ? in the url.
Query string : pqr=abcd
zzz.java
package mservlet;
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res) throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String aa;
aa = req.getParameter("pqr");
out.println("<HTML>");
out.println( "pqr is " + aa);
out.println("</HTML>");
out.close();
}
}
http://127.0.0.1:8080/webapps-simple/zzz?pqr=abcd
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.
pqr is abcd
This is how the servlet can figure out what the user has typed. Once you press enter, the screen will show ‘pqr is abcd’. If instead of abcd you had initialized pqr to abcd xyz then aa would return abcd xyz.
http://127.0.0.1:8080/webapps-simple/zzz?pqr=abcd xyz
The browser screen would have
pqr is abcd xyz
and the url would change to
http://127.0.0.1:8080/webapps-simple/zzz?pqr=abcd%20xyz
There is %20 in the place of a space. At times instead of a '%' sign it may also show a '+' sign. 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.
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 action="http://127.0.0.1:8080/aa.html">
<input type=text name=pqr>
<input type=submit value=search>
</form>
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 display 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, if you want to search for some word in a search engine, you will type that word in the text box 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.
Now edit your a.html file to match the following
a.html
<form action="http://127.0.0.1:8080/aa.html">
<input type=text name=pqr>
<input type=text name=aa>
<input type=submit value=search>
</form>
Here, we have an additional text box called aa and a button called submit.
Excecute it from the browser. Type abcd in the first text box and efgh in the second. Now click on the submit button. It will display for you the following url
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
package mservlet;
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res) throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String aa,bb;
aa = req.getParameter("pqr");
bb = req.getParameter("xyz");
out.println("<HTML>");
out.println( "pqr is " + aa + " xyz " + bb);
out.println("</HTML>");
out.close();
}
}
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
<html>
<b>Test</b>
<form action="http://127.0.0.1:8080/webapps-simple/zzz" method="get">
<input type=text name="pqr">
<input type=text name="xyz">
<input type=submit value="Click...">
</form>
</html>
Save the html file in C:\Sun\AppServer\domains\domain1\docroot>. Then, in the browser enter the url as
http://127.0.0.1:8080/b.html
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.
Note the url in the address bar
http://127.0.0.1:8080/webapps-simple/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 details 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 Java Web Server has a large number of properties. A property is a name with 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. In such cases where the definit count is not available, , 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. Lets look at the next program which demonstrates this feature.
zzz.java
package mservlet;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("Parameters:<p>");
Enumeration pNames = req.getParameterNames();
while (pNames.hasMoreElements()) {
String name = (String) pNames.nextElement();
String[] val = req.getParameterValues(name);
out.println(" " + name + ":");
for (int i = 0; i < val.length; i++) {
out.println(" " + val[i] + "<p>");
}
}
}
}
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.
Whenever we have more than one name/value pair, the function returns an enumeration which then uses the same while loop to display all the multiple values. Enumeration makes the programmers life easier.
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
package mservlet;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("Request headers:");
Enumeration hNames = req.getHeaderNames();
while (hNames.hasMoreElements()) {
String name = (String) hNames.nextElement();
String value = req.getHeader(name);
out.println(" " + name + " : " + value);
out.println("<br>");
}
out.println();
out.println("Cookies:");
Cookie[] cookies = req.getCookies();
if (cookies == null)
out.println("None");
else
for (int i = 0; i < cookies.length; i++) {
String name = cookies[i].getName();
String value = cookies[i].getValue();
out.println(" " + name + " : " + value);
}
}
}
Output
Request headers: accept : image/gif, image/x-xbitmap, image/jpeg,
image/pjpeg, application/vnd.ms-powerpoint, application/vnd.ms-excel,
application/msword, application/x-shockwave-flash, */*
accept-language : en-us
accept-encoding : gzip, deflate
user-agent : Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR
1.0.3328)
host : 127.0.0.1:8080
connection : Keep-Alive
Cookies: None
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 a list of headers 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
package mservlet;
import javax.servlet.http.*;
import java.io.*;
import java.net.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res) throws IOException
{
URL u;URLConnection c;InputStream n;DataInputStream d;String s;
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML>");
out.println("<head><title>Hello World</title></head>");
out.println("<body>");
out.println("<h1>Hello World</h1>");
try {
u=new URL("http://www.yahoo.com");
c = u.openConnection();
n = c.getInputStream();
d=new DataInputStream(n);
while ((s=d.readLine()) != null )
out.println(s);
n.close();
}
catch(Exception x){}
out.println("</body></html>");
out.close();
}
}
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 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
package mservlet;
public class zzz extends Servlet
{
}
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\
WEB-INF\classes\mservlet> javac zzz.java
zzz.java:1: cannot resolve symbol
symbol : class Servlet
location: class zzz
public class zzz extends Servlet
^
1 error
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.
Coming back to our program, let's compile it.
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
package mservlet;
import javax.servlet.*;
public class zzz extends Servlet
{
}
>javac zzz.java
zzz.java:2: no interface expected here
public class zzz extends Servlet
^
1 error
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
package mservlet;
import javax.servlet.*;
public class zzz implements Servlet
{
}
>javac zzz.java
zzz.java:2: zzz is not abstract and does not override abstract method service(ja
vax.servlet.ServletRequest,javax.servlet.ServletResponse) in javax.servlet.Servl
et
public class zzz implements Servlet
^
1 error
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 an errors. Runnable had only one function called run, this one has 5 functions. For the time being it is showing us only one error. The service method is to be implemented in the file. So we modify our program to bring in this method as in:
zzz.java
package mservlet;
import javax.servlet.*;
public class zzz implements Servlet
{
public void service(ServletRequest req,ServletResponse res)
{
System.out.println("service");
}
}
>javac zzz.java
zzz.java:2: zzz is not abstract and does not override abstract method init(javax
.servlet.ServletConfig) in javax.servlet.Servlet
public class zzz implements Servlet
^
1 error
Tagging a class with implements Servlet guarantees the class to looks like the Servlet class. Thus, it must define all the five functions present in the Servlet class. The Java Web server expects a class derived from Servlet, therefore it is a must to have the same five functions. The five errors that pop up one after the other would be
zzz.java:2: class zzz must be declared abstract. It does not define void destroy() from interface javax.servlet.Servlet.
public class zzz implements Servlet
^
zzz.java:2: class zzz must be declared abstract. It does not define void service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) from interface javax.servlet.Servlet.
public class zzz implements Servlet
^
zzz.java:2: class zzz must be declared abstract. It does not define void init(javax.servlet.ServletConfig) from interface javax.servlet.Servlet.
public class zzz implements Servlet
^
zzz.java:2: class zzz must be declared abstract. It does not define java.lang.String getServletInfo() from interface javax.servlet.Servlet.
public class zzz implements Servlet
^
zzz.java:2: class zzz must be declared abstract. It does not define javax.servlet.ServletConfig getServletConfig() from interface javax.servlet.Servlet.
public class zzz implements Servlet
^
5 errors
So, we 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
package mservlet;
import javax.servlet.*;
public class zzz implements Servlet
{
ServletConfig a;
public void destroy()
{
System.out.println("destroy");
}
public String getServletInfo()
{
System.out.println("getServletInfo");
return "";
}
public void service(ServletRequest req,ServletResponse res)
{
System.out.println("service");
}
public void init(ServletConfig a)
{
System.out.println("init");
}
public ServletConfig getServletConfig()
{
System.out.println("getServletConfig");
return (ServletConfig) a;
}
}
Well, it does work! It shows a valid blank screen. But to actually confirm that the servlet has worked, open up the server log file. 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.
[#|2004-10-06T11:10:26.638+0530|INFO|sun-appserver-pe8.0|javax.enterprise.system.stream.out|_ThreadID=11;|
init|#]
[#|2004-10-06T11:10:29.562+0530|INFO|sun-appserver-pe8.0|javax.enterprise.system.stream.out|_ThreadID=11;|
service|#]
A function returning void must not have a return value. But if it returns a String, then it must be a meaningful string or an empty string (which is two double quotes). When the function returns a ServletConfig then the return value must be a ServletConfig.
We will verify the above with a return 0 for all the functions as shown in the following program.
zzz.java
package mservlet;
import javax.servlet.*;
public class zzz implements Servlet
{
ServletConfig a;
public void destroy()
{
System.out.println("destroy");
}
public String getServletInfo()
{
System.out.println("getServletInfo");
return "";
}
public void service(ServletRequest req,ServletResponse res)
{
System.out.println("service");
}
public void init(ServletConfig a)
{
System.out.println("init");
}
public ServletConfig getServletConfig()
{
System.out.println("getServletConfig");
return 0;
}
}
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes\mservlet>javac zzz.java
zzz.java:27: incompatible types
found : int
required: javax.servlet.ServletConfig
return 0;
^
1 error
Thus it proves that the function must return a ServletConfig.
For this purpose, '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.
zzz.java
package mservlet;
import javax.servlet.*;
public class zzz implements Servlet
{
public void destroy()
{
System.out.println("destroy");
}
public String getServletInfo()
{
System.out.println("getServletInfo");
return "";
}
public void service(ServletRequest req,ServletResponse res)
{
System.out.println("service");
}
public void init(ServletConfig a)
{
System.out.println("init");
}
public ServletConfig getServletConfig()
{
ServletConfig a;
a=new ServletConfig();
System.out.println("getServletConfig");
return (ServletConfig)a;
}
}
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes\mservlet>javac zzz.java
zzz.java:24: javax.servlet.ServletConfig is abstract; cannot be instantiated
a=new ServletConfig();
^
1 error
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.
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.
zzz.java
package mservlet;
import java.io.*;
import javax.servlet.*;
public class zzz implements Servlet
{
ServletConfig a;
public void destroy()
{
System.out.println("destroy");
}
public String getServletInfo()
{
System.out.println("getServletInfo");
return "";
}
public void service(ServletRequest req,ServletResponse res)
{
try
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<b>Bye");
}
catch (Exception e){}
System.out.println("service");
}
public void init(ServletConfig a)
{
System.out.println("init");
}
public ServletConfig getServletConfig()
{
System.out.println("getServletConfig");
return (ServletConfig) a;
}
}
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.
The Java Web server calls service. Service has the getparameter function to figure out whether the http method was 'post' or 'get'. It is called with the same parameters and it puts these on the stack. It in turn calls doGet or doPost. The destroy gets called only at the end. Remember, with get, the parameters are sent with the url as one packet, but with post, a seperate packet is sent.
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
package mservlet;
import javax.servlet.*;
public class zzz extends GenericServlet
{
}
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes\mservlet>javac zzz.java
zzz.java:3: mservlet.zzz is not abstract and does not override abstract method s
ervice(javax.servlet.ServletRequest,javax.servlet.ServletResponse) in javax.serv
let.GenericServlet
public class zzz extends GenericServlet
^
1 error
The earlier versions had provided us with the source code for GenericServlet.java. Anyways, GenericServlet class has the service function but it is declared abstract. So, we will have to write our own service function. We will explain what we mean by that with the help of an example.
xxx.java
interface xxx
{
public void aaa();
public void bbb();
}
>javac xxx.java
yyy.java
public abstract class yyy implements xxx
{
public void aaa()
{
}
public abstract void bbb();
}
>javac yyy.java
If you get errors stating it cannot resolve symbol xxx , then ensure that your classpath variable is set to the current directory as well.
set CLASSPATH=.;%CLASSPATH%
Now let's create ppp.java and extend from yyy.
ppp.java
import java.io.*;
public class ppp extends yyy
{
}
C:\Sun\AppServer\domains\domain1\applications\j2ee-modules\webapps-simple\WEB-INF\classes\mservlet>javac ppp.java
ppp.java:2: ppp is not abstract and does not override abstract method bbb() in yyy
public class ppp extends yyy
^
1 error
Compiling ppp.java results in the same error as seen with 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.
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. So make the following changes in ppp.java
ppp.java
import java.io.*;
public class ppp extends yyy
{
public void bbb()
{
System.out.println("hi");
}
}
Since bbb is abstract, you cannot call it directly. Therefore bbb is overridden with our own function. Now compile it and you will see no errors.
Similarly, for our previous program we have to override the service function . The next program demonstrates the same. Two functions, service and init are defined in class zzz.
zzz.java
package mservlet;
import java.io.*;
import javax.servlet.*;
public class zzz extends GenericServlet
{
public void service(ServletRequest req,ServletResponse res)
{
try
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<b>Bye");
}
catch (Exception e){}
System.out.println("service");
}
public void init(ServletConfig a)
{
System.out.println("init");
}
}
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. GenericServlet makes writing servlets much easier.
The following html file will create a simple form where the method used is 'post'. So within the servlet, function doPost will be called.
C:\Sun\AppServer\domains\domain1\docroot>notepad a.html
a.html
<html>
<form action=/servlet/zzz method=post>
<input type=submit value="Click...">
</form>
</html>
zzz.java
package mservlet;
import java.io.*;
import javax.servlet.http.*;
public class zzz extends HttpServlet
{
public void doGet(HttpServletRequest req,HttpServletResponse res)
{
try
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<b>Get");
}
catch (Exception e){}
}
public void doPost(HttpServletRequest req,HttpServletResponse res)
{
try
{
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<b>Post");
}
catch (Exception e){}
}
}
When you change the method to 'get', doGet will be called, which will print get in the window.
http://127.0.0.1:8080/a.html
Remember, the html file must be in C:\Sun\AppServer\domains\domain1\docroot>. 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'.
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.