JAVA SERVLETS
Introduction
How a HTTP request work
How a Servlet is invoked
The Servlet API
A HelloWorld Servlet
A Test Environment Servlet
A form processing servlet
Thread Safe
The init method
The destroy method
The log method
The getServletInfo method
Introduction
A Java servlet is a server side application that communicates with clients. The Java servlet API is not included in the core Java framework instead it is a standard extension. The Servlet API can be downloaded from
And the javadoc documentation can be viewed online at
The file servlet_tutorial.html which is include with the Servlet API is the material you need to read (it's about 15 pages) for the exercise. The tutorial explains in more detail about how servlets works. A Java Servlet is included in a server, usually a web server, to extend it in some manner. Servlets are an effective substitute for CGI scripts. They provide a way to generate dynamic documents that is both easier to write, maintain and faster to run. Servlets are to servers what applets are to browsers.
This lecture and course will focus on the http side of servlets but servlet is much more than just a http thing. You can even implement an own transport protocol for servlets.
How a HTTP request work
When you type a URL in a browser type Netscape like
and submits it the server gets a request like this:
GET /index.html HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.07 [en] (WinNT; I)
Host: asterix.laudanum.net
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
The first line is the HTTP request method (usually GET or POST) and the path to the requested file. The rest is some data of the browser and the connection. The server reads the requested file and sends the content of the file over the network with some META data. The browser actually gets an answer like this:
HTTP/1.1 200 OK
Date: Fri, 09 Apr 1999 18:22:54 GMT
Server: Apache/1.2.6 Red Hat
Last-Modified: Thu, 07 May 1998 18:17:09 GMT
ETag: "755-792-3551faa5"
Content-Length: 1938
Accept-Ranges: bytes
Connection: close
Content-Type: text/html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<HTML>
<HEAD>
<TITLE>Test Page for Red Hat Linux's Apache Installation</TITLE>
</HEAD>
---- and the rest of the html document ----
The lines until the empty line is called the request META information. The first line is the status header (200 is "ok" and 404 is "not found"; you find all the status codes in the interface javax.servlet.http.HttpServletResponse), the follow lines until the empty line is some name value pairs about the request. The most important ones are Content-Type and Content-Length. The CONTENT-TYPE says what kind of data the response have, text/html and text/plain is some examples. CONTENT-LENGTH should say the length of the response data in bytes. After the first empty line the data should begin. For details about http look at
How a Servlet is invoked
The server usually has a path for all servlets like
So if the server had a counter servlet it would be invoked with
The first time a servlet is requested the servlet is loaded in to the servlet engine and stays there, until the server process stops or the server administrator restarts the servlet engine or reloads the servlet. When the servlet is loaded in to the servlets engine the servlet init method is called. When the servlet is loaded and initialized it calls the service method for each request. So the init method is called only once under a Servlet lifetime but the service method is called for each request. When the Servlet is unloaded the destroy method is called.
The Servlet API
The servlet API consist of two packages
- javax.servlet
- javax.servlet.http
The javax.servlet package is intended for a generic servlet that perhaps implements an own protocol. The javax.servlet.http package is intended for http requests and responses. The focus of this course will be on the javax.servlet.http package.
The javax.servlet.http package consists of 5 interfaces and 4 classes
Interfaces
- HttpServletRequest
- HttpServletResponse
- HttpSession
- HttpSessionBindingListener
- HttpSessionContext
Classes
- Cookie
- HttpServlet
- HttpSessionBindingEvent
- HttpUtils
A HelloWorld Servlet
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest req,HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res. getWriter();
out.println("<HTML<HEAD<TITLE>Hello World</TITLE</HEAD>");
out.println("<BODY>");
out.println("Hello World");
out.println("</BODY</HTML>");
}
}
The basic think is to extend your class from HttpServlet and override the HTTP method you want to implement (doGet, doPost, doPut, doOptions, doDelete and doTrace) the HttpServer service method will dispatch the request to those methods.
Another thing that is good to do is to override the Java method getServletInfo this should return a string about the servlet its author, version, copyright and such stuff.
To run this servlet with the servletrunner from Sun you should have servlets.properties file like this:
servlet.HelloWorld.code=HelloWorld
If you save the Java file in directory c:\Servlets. Run the servletrunner from that directory with the switch -d c:\Servlets like this:
C:\Servlets\servletrunner.exe -d C:\servlets
Then open Netscape and type the url and you should se a page saying Hello World. The localhost can be changed with your machine IP or host name. :8080 is the server port, the serverrunner runs on port 8080 for default and the standard HTTP server port is 80. So when you type a url it's equally to
For more info on servletrunner look at the servlet tutorial.
A Test Environment Servlet
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestEnv extends HttpServlet {
private Hashtable initParam;
public void init(ServletConfig config) throws ServletException {
super.init(config);//THIS LINE IS MOST IMPORTANT!!!!!!
/* IF YOU OVERRIDE init always call super.init(config) */
this.initParam=new Hashtable();
Enumeration enum=config.getInitParameterNames();
String str;
while(enum.hasMoreElements()) {
str=(String) enum.nextElement();
this.initParam.put( str,config.getInitParameter(str) );
}
}
public void doGet(HttpServletRequest req,HttpServletResponse res)
throws ServletException, IOException {
Enumeration enum=req.getHeaderNames();
String str;
PrintWriter out = res. getWriter();
out.println("HTTP HEADERS:");
while(enum.hasMoreElements()) {
str=(String)enum.nextElement();
out.println(str + " = " + req.getHeader(str));
}
out.println("\nSome CGI variables:");
out.println("AUTH_TYPE = " + req.getAuthType());
out.println("REQUEST_METHOD = " + req.getMethod());
out.println("PATH_TRANSLATED = " + req.getPathTranslated());
out.println("PATH_INFO = " + req.getPathInfo());
out.println("QUERY_STRING = " + req.getQueryString());
out.println("SCRIPT_NAME = " + req.getServletPath());
out.println("CONTENT_LENGTH = " + req.getContentLength());
out.println("CONTENT_TYPE = " + req.getContentType());
out.println("SERVER_PROTOCOL = " + req.getProtocol());
out.println("REMOTE_ADDR = " + req.getRemoteAddr());
out.println("REMOTE_HOST = " + req.getRemoteHost());
out.println("SERVER_NAME = " + req.getServerName());
out.println("SERVER_PORT = " + req.getServerPort());
out.println("\nThe initialization parameters");
enum=initParam.keys();
while(enum.hasMoreElements()) {
str=(String)enum.nextElement();
out.println(str + " = " + initParam.get(str));
}
}
}
The servlet.properties file looks like this:
servlet.TestEnv.code=TestEnv
servlet.TestEnv.initArgs= \
filename=foo.java,\
type=bar
And the output in the Netscape looks like this:
HTTP HEADERS:
Connection = Keep-Alive
User-Agent = Mozilla/4.07 [en] (WinNT; I)
Host = localhost:8080
Accept = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding = gzip
Accept-Language = en
Accept-Charset = iso-8859-1,*,utf-8
Some CGI variables:
AUTH_TYPE = null
REQUEST_METHOD = GET
PATH_TRANSLATED = .\examples\hello
PATH_INFO = /hello
QUERY_STRING = null
SCRIPT_NAME = /servlet/TestEnv
CONTENT_LENGTH = -1
CONTENT_TYPE = null
SERVER_PROTOCOL = HTTP/1.0
REMOTE_ADDR = 127.0.0.1
REMOTE_HOST = localhost
SERVER_NAME = localhost
SERVER_PORT = 8080
The initialization parameters
filename = foo.java
type = bar
A form processing servlet
An HTML Form on an html page is used to send some kind of data up to the server for processing. Before Java Servlets it usually was a CGI script on the server that processed that data. Now the world is rapidly moving over to Java Servlets. Why?
The HTML form tag looks like this:
<FORM ACTION="TestForm" METHOD="POST">
where action is the url to your processing servlet or script and the method is usually POST when you send data to the server but you can also use GET, for difference on GET and POST look at
Basic rule:Use GET when you get something from the server and POST when you send data to the server.
Then you have a group of name value pairs in your HTML form.
Take a look at this guest book form:
The HTML source:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>A Form Example</TITLE>
</HEAD>
<BODY BACKGROUND="" BGCOLOR="#ffffff" TEXT="#000000" LINK="#0000ff" VLINK="#800080" ALINK="#ff0000">
<h1>A Guest book</h1>
<TABLE border="0">
<FORM ACTION="/servlet/GuestBook" METHOD="POST">
<tr<td>Name:</TD<td<INPUT TYPE="text" NAME="name" SIZE="40" MAXLENGTH="80" VALUE=""</INPUT</td</tr>
<tr<td>E-mail:</td<td<INPUT TYPE="text" NAME="email" SIZE="40" MAXLENGTH="80" VALUE=""</INPUT</Td</tr>
<tr<td>HomePage:</td<td<INPUT TYPE="text" NAME="homepage" SIZE="40" MAXLENGTH="80" VALUE=""</INPUT</td</tr>
<tr<td>Sex:</td<td> Man<INPUT TYPE="radio" NAME="sex" VALUE="Man"</INPUT>
Woman<INPUT TYPE="radio" NAME="sex" VALUE="Woman"</INPUT</td</tr>
<tr<td valign=top>Comments:</td<td<TEXTAREA NAME="comments" ROWS="5" COLS="40"</TEXTAREA</td</tr>
<tr<td colspan=2<INPUT TYPE="submit"</INPUT</td</tr>
</FORM>
</TABLE>
</BODY>
</HTML>
This form has five name value pairs, three Text fields, one radio button and one Text Area. The user who fills in the form enters the values in each fields and press the submit button. The request to server looks like this.
POST /servlet/GuestBook HTTP/1.0
Referer:
Connection: Keep-Alive
User-Agent: Mozilla/4.07 [en] (WinNT; I)
Host: obelix
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Content-type: application/x-www-form-urlencoded
Content-length: 90
name=foo+bar&email=foo%40bar.com&homepage=
By the way the request would look like this if the method is GET not POST
GET /servlet/GuestBook?name=foo+bar&email=foo%40bar.com&homepage=
&sex=Man&comments=This+is+a+comment HTTP/1.0
Referer:
Connection: Keep-Alive
User-Agent: Mozilla/4.07 [en] (WinNT; I)
Host: obelix
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Now the servlets needs to read in the data from the request, you find the length of the data in the Content-Length header, extract the name value pairs and store it in the guest book at the server. The guest book can be a file on the server or a JDBC connected database. Here is a servlet that extract the name value pairs from the request and show it to the user as a new guest book entry. It doesn't save the entry anywhere.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class GuestBook extends HttpServlet {
public void doPost(HttpServletRequest req,HttpServletResponse res)
throws ServletException, IOException {
int length=req.getContentLength();
Hashtable table= HttpUtils.parsePostData(length,req.getInputStream());
String line;
String value;
BufferedWriter out;
res.setContentType("text/html");
out=new BufferedWriter(res.getWriter());
line="<HTML<HEAD<TITLE>The Great Guest Book</TITLE</HEAD<BODY>\n";
writeLine(out,line);
line="<TABLE border=1<CAPTION bgcolor=\"#555555\">A new Guest Book Entry</CAPTION>";
writeLine(out,line);
value=getValue("name",table);
if( !isEmpty(value) ) {
line="<TR<TD>Name:</TD<TD>" + value + "</TD</TR>";
writeLine(out,line);
}
value=getValue("email",table);
if( !isEmpty(value) ) {
line="<TR<TD>E-mail:</TD<TD>" + value + "</TD</TR>";
writeLine(out,line);
}
value=getValue("homepage",table);
if( !isEmpty(value) ) {
line="<TR<TD>HomePage:</TD<TD>" + value + "</TD</TR>";
writeLine(out,line);
}
value=getValue("sex",table);
if( !isEmpty(value) ) {
line="<TR<TD>Sex:</TD<TD>" + value + "</TD</TR>";
writeLine(out,line);
}
value=getValue("comments",table);
if( !isEmpty(value) ) {
line="<TR<TD>Comments:</TD<TD>" + value + "</TD</TR>";
writeLine(out,line);
}
line="</TABLE</BODY</HTML>";
writeLine(out,line);
out.flush();
}
public void writeLine(BufferedWriter out,String line) throws IOException{
out.write(line,0,line.length());
}
public String getValue(String key,Hashtable table) {
String value[];
value=(String[])table.get(key);
return (value!=null) ? value[0]:null;
}
public boolean isEmpty(String value) {
if( value==null || value.equals("") )
return true;
return false;
}
}
The Response looks like this:
Thread Safe
Servlets can run multiple service methods at a time. It is important, therefore, that service methods are written in a thread-safe manner. For example, if a service method might update a field in the servlet object, that access should be synchronized. If, for some reason, a server should not run multiple service methods concurrently, the servlet should implement the SingleThreadModel interface. This interface guarantees that no two threads will execute the servlet's service methods concurrently.
Look in the servlet tutorial for more information.
The init method
public void init(ServletConfig config) throws ServletException
Initializes the servlet and logs the initialization. The init method is called once, automatically, by the network service each time it loads the servlet. It is guaranteed to finish before any service requests are accepted. On fatal initialization errors, an UnavailableException should be thrown. Do not call the method System.exit. The init method stores the ServletConfig object. Servlet writers who specialize this method should call either super.init, or store the ServletConfig object themselves. If an implementor decides to store the ServletConfig object in a different location, then the getServletConfig method must also be overridden.
The destroy method
public void destroy()
Destroys the servlet, cleaning up whatever resources are being held, and logs the destruction in the servlet log file. This method is called, once, automatically, by the network service each time it removes the servlet. After destroy is run, it cannot be called again until the network service reloads the servlet. When the network service removes a servlet, it calls destroy after all service calls have been completed, or a service-specific number of seconds have passed, whichever comes first. In the case of long-running operations, there could be other threads running service requests when destroy is called. The servlet writer is responsible for making sure that any threads still in the service method complete.
The log method
public void log(String msg)
Writes the class name of the servlet and the given message to the servlet log file. The name of the servlet log file is server specific, it is normally an event log.
The getServletInfo method
public String getServletInfo()
Returns a string that contains information about the servlet, such as its author, version, and copyright. This method must be overridden in order to return this information. If it is not overridden, null is returned.