Servlet

What is a Servlet?

Servlet is a Java-based technology for creating programs such as bulletin boards. Just as the java.sql and javax.sql packages are called JDBC, the javax.servlet and javax.servlet.http packages are called Servlet. Servlet is independent of the network protocol but mainly used to create server-side components which operate in the HTTP protocol environment.

The basic structure of Servlet

The basic structure of Servlet consists of:

  • The javax.servlet.Servlet interface which all servlets must implement
  • The javax.servlet.GenericServlet abstract class which most servlets inherit
  • The javax.servlet.http.HttpServlet class which servlets operate in the HTTP protocol inherit

As shown in the figure below, GenericServlet implements the javax.servlet.ServletConfig interface.
Servlets Framework
The figure does not show all properties and methods of GenericServlet and HttpServlet.

Servlet Interface

The javax.servlet.Servlet interface is the heart of a Servlet architecture. All servlets must implement this interface. The Servlet interface declares the lifecycle methods of Servlet.

  • init() initializes the servlet
  • service() services to client requests
  • destroy() stops service, returns resources

init()

The servlet container calls the init() method once after it created the servlet object. A servlet can service only after the init() method completes without errors. Before the init() completes, requests are blocked. The init() method receives the object of the ServletConfig interface type as an argument. If you set the Servlet initialization parameters in web.xml, this ServletConfig will have servlet initialization information set in web.xml. If there is servlet initialization information, you have to write the code to initialize the servlet in the body of the init() method.

void init(ServletConfig config) throws ServletException;

service()

A client sends a request to a servlet. The servlet container intercept the client's request and then calls the corresponding servlet's service() method. The service() method reads the request information from the ServletRequest type instance that is the first argument and responds to the client using the ServletResponse type instance that is the second argument.

Note that the servlet container executes the service() method on a new thread whenever a client sends a request. So a servlet can respond to a large number of client requests without delay. However, the critical section problem can arise with resources that the servlet uses (files, network connections, static variables, instance variables, etc.). Synchronizing resources used by the servlet is not good code in most cases. It is best not to create static or instance variables within the servlet class's body.

void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

destroy()

It is called by the servlet container when the servlet is no longer in service. This method is not what the programmer calls in code. If you unload the web application using the tomcat manager or shut down Tomcat, the destroy() of servlets executes. The tomcat manager is a web application that you can access at http://localhost:8080/manager. You can manage web applications with the tomcat manager. The tomcat manager needs you to log in with the administrator id and password you set when installing Tomcat. If you do not remember your administrator id and password, you can see that in the CATALINA_HOME/conf/tomcat-users.xml.

void destroy();

GenericServlet abstract class

Most servlets inherit GenericServlet. GenericServlet implements Servlet and ServletConfig interface. Because GenericServlet does not implement the service() method of Servlet interface, GenericServlet's service() method remains an abstract method, so GenericServlet is an abstract class. Subclasses that inherit GenericServlet must implement the following service() method.

public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;

The init(ServletConfig config) method is implemented in GenericServlet as follows:

public void init(ServletConfig config) throws ServletException {
  this.config = config;
  this.init();
}

The init(ServletConfig config) method calls the init() method with no parameters on the last line. The parameterless init() method was added to the GenericServlet for convenience. This method has nothing in the method body like below.

public void init() throws ServletException {

}

You better override the parameterless init() rather than the init(ServletConfig config) in the subclass because you don't have to worry about storing the ServletConfig object. if you overrie the init(ServletConfig config) method in a subclass, you need to add super.init(config); code on the first line. Without this code, the servlet will not store the ServletConfig object.

The init(ServletConfig config) method saves the ServletConfig object received as an argument in the instance variable config. The getServletConfig() method of GenericServlet returns this variable's value.

public ServletConfig getServletConfig() {
  return config;
}

The getServletContext() is method of ServletConfig interface. GenericServlet implements ServletConfig interface. The getServletContxt() method of GenericServlet returns an object of type ServletContext.

public ServletContext getServletContext() {
  return getServletConfig().getServletContext();
}

The getInitParameter() and getInitParameterNames() methods of the ServletConfig interface are implemented as follows in the GenericServlet.

public String getInitParameter(String name) {
  return getServletConfig().getInitParameter(name);
}
public Enumeration getInitParameterNames() {
  return getServletConfig().getInitParameterNames();
}   

The Enumeration interface has two methods to access the data in order: hasMoreElements() and nextElement().

It is for the convenience of the programmer that GenericServlet implements ServletConfig. It is easier to use this.getServletContext() rather than this.getServletConfig().GetServletContext() to get the reference to an object of type ServletContext in servlet. It is easier to use String driver = this.getInitParameter("driver") rather than String driver = this.getServletConfig().GetInitParameter("driver") to get servlet's initialization parameter information.

HttpServlet

Servlet responding HTTP requests should inherit HttpServlet abstract class. HttpServlet inherits GenericServlet abstract class. HttpServlet provides methods for handling HTTP requests.

The service() of HttpServlet has the following two parameters.

  • HttpServletRequest
  • HttpServletResponse

When the user's request reaches the server, the servlet container intercepts the request, creates two instances. One is the object of type HttpServletRequest from the user's request information. Another is the object of type HttpServletResponse required when the servlet sends the result to the user. The servlet container delivers created two objects to the servlet by calling the service(ServletRequest req, ServletResponse resp) method and this method the service(HttpServletRequest req, HttpServletResponse resp) method.

HttpServlet class implements the GenericServlet's service() abstract method by calling the protected void service(HttpServletRequest req, HttpServletResponse resp) method.

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
	
  HttpServletRequest  request;
  HttpServletResponse response;
	
  try {
    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
  } catch (ClassCastException e) {
    throw new ServletException("non-HTTP request or response");
  }
	
  service(request, response);
}

Eventually, the following method handles the HTTP request.

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

  String method = req.getMethod();

  if (method.equals(METHOD_GET)) {
    long lastModified = getLastModified(req);
    if (lastModified == -1) {
      // servlet doesn't support if-modified-since, no reason
      // to go through further expensive logic
      doGet(req, resp);
    } else {
      long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
      if (ifModifiedSince < (lastModified / 1000 * 1000)) {
        // If the servlet mod time is later, call doGet()
        // Round down to the nearest second for a proper compare
        // A ifModifiedSince of -1 will always be less
        maybeSetLastModified(resp, lastModified);
        doGet(req, resp);
      } else {
        resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
      }
    }
  } else if (method.equals(METHOD_HEAD)) {
    long lastModified = getLastModified(req);
    maybeSetLastModified(resp, lastModified);
    doHead(req, resp);
  } else if (method.equals(METHOD_POST)) {
    doPost(req, resp);
  } else if (method.equals(METHOD_PUT)) {
    doPut(req, resp);
  } else if (method.equals(METHOD_DELETE)) {
    doDelete(req, resp);
  } else if (method.equals(METHOD_OPTIONS)) {
    doOptions(req,resp);
  } else if (method.equals(METHOD_TRACE)) {
    doTrace(req,resp);
  } else {
    //
    // Note that this means NO servlet supports whatever
    // method was requested, anywhere on this server.
    //
    String errMsg = lStrings.getString("http.method_not_implemented");
    Object[] errArgs = new Object[1];
    errArgs[0] = method;
    errMsg = MessageFormat.format(errMsg, errArgs);

    resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
  }
}	

The only thing the HttpServlet's +service() method does is call the #service() method. (Where + denotes the public access modifier and # the protected access modifier)

When the #service() method of HttpServlet class runs, this method reads the HTTP METHOD (POST, GET etc.) in the request object (an object of type HttpServletRequest) and calls the matching method according to this value. For example, if the HTTP method is "GET", it calls doGet() and if "POST", it calls the doPost() method. Methods like doGet() and doPost() are the methods we need to override.

HttpServletRequest interface inherits ServletRequest interface. HttpServletResponse interface inherits ServletResponse interface. A vendor making servlet container makes classes that implement HttpServletRequest and HttpServletResponse interfaces.

Summary of Servlet API

Servlet Interface
init(ServletConfig config)
service(ServletRequest req, ServletResponse resp)
destroy()
getServletConfig():ServletConfig
Returns a ServletConfig object, which contains initialization and startup parameters for this servlet.
getServletInfo():String
Return simple information about this servlet
ServletConfig Interface
getInitParameter(String name):String
Gets the value of the initialization parameter with the given name.
getInitParameterNames():Enumeration
Return the names of the servlet's initialization parameters as an Enumeration of String objects, or an empty Enumeration if the servlet has no initialization parameters.
getServletContext():ServletContext
Return a reference to the ServletContext
getServletName():String
Return the name of this servlet instance
+GenericServlet Abstract class
This class defines a generic, protocol independent servlet. This class implements Servlet and ServletConfig interface.
+init()
Servlet initialization method, which is called by GenericServlet's init(ServletConfig config) method
<<abstract>> +service(ServletRequest req, ServletResponse res)
The GenericServlet abstract class still does not implement the service() method of the Servlet interface.
HttpServlet Abstract class
Inherits GenericServlet.
#doGet(HttpServletRequest req, HttpServletResponse resp)
Method to handle HTTP GET requests
#doPost(HttpServletRequest req, HttpServletResponse resp)
Method to handle HTTP POST
+service(ServletRequest req, ServletResponse res)
This method overrides GenericServlet's abstract method service(). This method does nothing other than call the #service() method.
#service(HttpServletRequest req, HttpServletResponse resp)
Call one of doGet(req, resp), doHead(req, resp), doPost(req, resp), doGet(req, resp), doDelete(req, resp), doOptions(req, resp), doTrace(req, resp) according to the HTTP METHOD.
ServletContext Interface
This interface provides a set of methods that a servlet uses to communicate with its servlet container. These methods do following:
  • Get the MIME type of a file
  • Get the full path of a file
  • Dispatch requests
  • Write to a log file
MIME means Multipurpose Internet Mail Extensions, for example, MIME of .html is text/html, .txt is text/plain, .gif is image/gif.
There is one ServletContext instance per web application. This instance serves as a common repository for dynamic components. In other words, data stored in the ServletContext is freely accessible form servlets and JSPs within the same web application.
setAttribute(Strng name, Object value)
Stores data as a name-value pair.
getAttribute(String name):Object
Returns data with the given name.
removeAttribute(String name)
Removes the data with the given name.
getInitParameter(String name):String
Returns the value of the web application initialization parameter with the given name.
getRequestDispatcher(String path):RequestDispatcher
Returns a RequestDispatcher object for the resource located at the given path.
getRealPath(String path):String
Returns the real path corresponding to the given virtual path.
getResource(String path):URL
Returns a URL to the resource, which is mapping to the given path.
RequestDispatcher Interface
Used to sends requests from the client to any resource (such as a servlet, HTML file, or JSP file) on the server or adds another resource's contents to the response.
forward(ServletRequest req, ServletResponse res)
Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server.
include(ServletRequest req, ServletResponse res)
Includes the contents of a resource (servlet, JSP file, HTML file) in the response.
ServletRequest Interface
Contains client request information.
setAttribute(String name, Object o)
Stores data as a name-value pair in this request.
getAttribute(String name):Object
Returns the stored data with the given name.
removeAttribute(String name)
Removes the stored data with the given name.
getInputStream():ServletInputStream
Returns an input stream to read the binary data in the body of the request.
getParameter(String name):String
Returns the value of the HTTP parameter with the given name.
getParameterNames():Enumeration
Return all HTTP parameter names as an Enumeration type.
getParameterValues(String name):String[]
Returns all the values of the HTTP parameter with the given name as a String array. This method returns multiple values in a single HTTP parameter, such as a checkbox or a multiple-selection list.
getRemoteAddr():String
Returns the client's IP address.
HttpServletRequest Interface
Inherits ServletReqeust.
getCookies():Cookie[]
Returns an array containing all cookie objects the client sent with this request.
getSession():HttpSession
Returns current session associated with this request, or if the request does not have a session, creates one.
getSession(boolean create):HttpSession
Returns the current session associated with this request or, if there is no current session and create's value is true, returns a new session. If create is false, returns null.
getContextPath():String
Returns the part of the request URI that indicates the context of this request. If you request http://localhost:8080/contextPath/board/list.jsp?page=1, it will return "/contextPath".
getRequestURI():String
If you request http://localhost:8080/ContextPath/board/list.jsp?page=1, it will return "/ContextPath/board/list.jsp".
getServletPath():String
Returns the part of this request's URL that calls the servlet.
getQueryString():String
If you request http://localhost:8080/ContextPath/board/list.jsp?page=1, it will return "page=1".
ServletResponse Interface
Used to send a response to client.
getOutputStream():ServletOutputStream
Returns an output stream for writing binary data in the response.
getWriter():PrintWriter
Returns an output stream for writing character text in the response.
setContentType(String type)
Sets the content type of the response being sent to the client. (For example, if the response data is HTML, set MIME to text/html, if plain text, set MIME to text/plain, if binary data, set MIME to application/octet-stream) It should be called before the getWriter() method.
getContentType():String
Return the content type specified by the setContentType() method or returns null if not specified.
setCharacterEncoding(String charset)
Sets the character encoding (MIME charset) of the response. To set it to UTF-8, code as setCharacterEncoding ("UTF-8");. SetCharacterEncoding("UTF-8"); is equivalent to charset=UTF-8 in setContentType("text/html; charset=UTF-8"); in JSP code. It should be called before the getWrite() method is executed.
getCharacterEncoding():String
Returns the name of the character encoding (MIME charset) used for the body of this response. Returns "ISO-8859-1" if no character encoding is specified.
setContentLength(int length)
Sets the length of the content body in the response with an int value. This method sets the HTTP Content-Length header.
HttpServletResponse Interface
Inherits the ServletResponse interface. Used to send an HTTP response to the client.
addCookie(Cookie cookie)
Adds the specified cookie to the response header.
sendRedirect(String location)
Sends a temporary redirect response to the client using the given location URL.
HttpSession Interface
Used to store user information needed for session maintenance on the server-side.
getAttribute(String name):Object
setAttribute(String name, Object value)
removeAttribute(String name)
invalidate()
Cookie
Cookies are the information stored on the client-side for session maintenance. For maintaining the session, the web browser adds the cookie information to the request whenever it sends a request to the server that sent the cookie. Cookies can store multiple name-value pairs. Also, cookies can have optional values for path, domain, expiration date, and security. To create a cookie on a server-side component, you need to write code that adds a string of the promised format to the response header. A web browser that receives a response containing cookie information will also send cookie information whenever it sends a client request. The cookie information passed to the server-side component can be obtained as an array type using the getCookie() method of HttpServletRequest. Cookie or session is the technology that overcomes the HTTP protocol's limitations to disconnect after a response.
Cookie(String name, String value)
getName():String
getValue():String
setValue(String newValue)
getPath():String
setPath(String uri)
getDomain():String
setDomain(String pattern)
getMaxAge():int
setMaxAge(int expiry)
getSecure():boolean
setSecure(boolean flag)

Servlet Examples

Write all the examples below in the ROOT application. In the Creating a new web application, we changed the application with the document base C:/www/myapp to the ROOT application. Create JSPs in C:/www/myapp and java sources in C:/www/myapp/WEB-INF/src. Instead of using Eclipse, use a regular editor such as EditPlus.

SimpleServlet.java
package example;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    doPost(req,resp);
  }
	
  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) 
      throws ServletException, IOException {
			
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
    	
    out.println("<html>");
    out.println("<body>");
    	
    //Output the IP of client
    out.println("Your IP Address is " + req.getRemoteAddr() + ".\n");
    out.println("</body></html>");
    out.close();
  }
  
}

SimpleServlet does not implement servlet lifecycle methods init() and destroy(). These methods are already implemented in GenericServlet and are not visible in the above sources because there is no reason to override them. Open the /WEB-INF/web.xml file and add the servlet element to the subelement of the web-app element, as shown below.

web.xml
<servlet>
  <servlet-name>SimpleServlet</servlet-name>
  <servlet-class>example.SimpleServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>SimpleServlet</servlet-name>
  <url-pattern>/simple</url-pattern>
</servlet-mapping>

At the command prompt, navigate to the folder where SimpleServlet.java exists and compile as shown below.

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^
-cp "C:/apache-tomcat-9.0.87/lib/servlet-api.jar" ^
SimpleServlet.java
package javax.servlet.http does not exist
The above compilation error means that the Java compiler can not find the javax.servlet.http package. Because you did not add the full path to the servlet-api.jar file as the javac's cp option value, if the classpath you are adding as cp option's value contains whitespace character, you must enclose it with double quotes character (").

Restart Tomcat and visit http://localhost:8080/simple.

SimpleServlet.java source description

public class SimpleServlet extends HttpServlet

Servlets that inherit the HttpServlet class must be declared public.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  doPost(req, resp);
}

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  //.. omit ..
}

The doGet() and doPost() methods are methods that override the doGet() and doPost() methods of HttpServlet. A servlet that receives a GET request overrides the doGet() method. It is a request of the GET method to request the resource of the web server by inputting the address in the web browser. In the above example, the doGet() method calls the doPost() method. Therefore, the same code is executed whether it is a GET or a POST request. The doGet() and doPost() methods have HttpServletRequest and HttpServletResponse as parameters.

resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();

resp.setContentType("text/html; charset=UTF-8") sets the content type and character encoding of the document to be output to a web browser. This code can be used only once in servlet and must run before getting the PrintWriter object. You can obtain the PrintWriter instance by calling the getWriter() of HttpServletResponse.

out.println("<html>");
out.println("<body>");

//Output the IP of client
out.println("Your IP Address is " + req.getRemoteAddr() + ".\n");

SimpleServlet uses PrintWriter's println() method to send HTML code to the client. PrintWriter's println() method ouputs string passed as an argument to the client's web browser. HttpServletRequest's getRemoteAddr() is a method that returns the IP address of the client. HttpServeltRequest object contains information sent by the client and about the client.

Let's take a look at the process until the SimpleServlet servlet responds. The client uses the web browser to request the SimpleServlet. Tomcat intercepts the request and calls the +service(ServletRequest req, ServletResponse res) method of SimpleServlet. At this time, Tomcat passes the HttpServletRequest object that encapsulates the client's request and the HttpServletResponse object as method arguments to the +service() method. The +service(ServletRequest req, ServletResponse res) method merely calls the #service(HttpServletRequest req, HttpServletResponse resp) method. The #service() invokes methods such as doGet() or doPost() depending on HTTP Method (GET, POST, etc.). You requested SimpleServlet by typing directly into the web browser address bar. This request is a GET Method request. So the doGet() method runs.

How to send data to server-side resources and how server-side resources receive data from the client

In the Web environment, dynamic components are respond differently depending on the string data sent by the client. Web programmers who need to create dynamic web components need to know how to send string data to server-side resources using their web browser and obtain string data from clients on server-side resources. Web programmers mainly use form element and its subelements to allow clients to send string data to server-side resources. The web browser passes the client's data to the server-side resource specified by the form element's action attribute.

In the table below, the "HTML Form" item shows the HTML tag to receive data from the user, and the "Servlet" item shows code to get values from parameters sent by clients.

HTML Form Servlet
<input type="text" name="addr" />
req.getParameter("addr");
<input type="radio" name="os" value="Windows" />
<input type="radio" name="os" value="Linux" />
req.getParameter("os");
<input type="hidden" name="curPage" value="1" />
req.getParameter("curPage");
<input type="password" name="passwd" />
req.getParamter("passwd");
<textarea name="content" cols="60" rows="12">blah blah</textarea>
req.getParamter("content");
<select name="grade">
  <option value="A">A</option>
  <option value="B">B</option>
  <option value="C">C</option>
  <option value="D">D</option>
  <option value="F">F</option>
</select>
req.getParameter("grade");
<input type="checkbox" name="hw" value="Intel" />
<input type="checkbox" name="hw" value="AMD" />
req.getParameterValues("hw");
<select name="sports" multiple="multiple">
  <option value="soccer">Soccer</option>
  <option value="baseball">Baseball</option>
  <option value="basketball">Basketball</option>
</select>
req.getParameterValues("sports");

getParameter(String name)

The getParameter(String name) method of ServletRequest is the most common method used to get data sent by the web browser. The type of character data that the client sends to the server is a parameter name-value pair. Here, the name of the parameter is the value of the name attribute of the form's subelements (input, textarea, select, etc.), and value is the user input. In the server-side resource code, if the name of the parameter passed by the client is given as the argument of the getParameter(String name) method, this method returns the value that the user has entered or selected. An input element whose type attribute value is radio is called a radio button. Radio buttons with the same name attribute value can form a group and select only one item within a group.

getParameterValues(String name)

When a client sends multiple values with one parameter, the getParamterValues(String name) method of HttpServletRequest is needed to get these values on the server-side resource. This method returns an array of strings consisting of the values chosen by the user. You can send multiple values with one parameter with the following HTML elements:

  • <select multiple="multiple" ..><option..></option>..</select>
  • <input type="checkbox"../>

If the select element has multiple= "multiple" attribute, the user can select multiple options using the Ctrl or Shift key. An input element whose type attribute value is "checkbox" is called a checkbox. Multiple checkboxes with same name attribute value belong to the same group. A checkbox differs from a radio button in that multiple checkboxes within the same group can be selected.

getParamterNames()

To figure out what parameters sent by the user on the server-side components, you need the getParamterNames() method of HttpServletRequest. The getParameterNames() method returns an Enumeration object containing all parameter names.

<input type="file" ../>
The input element whose type attribute value is "file" transfers binary data such as images to the server. It's parent element, the form must have attributes such as method="post" enctype="multipart/form-data". If you need to send other additional string data (For example, name, title, content, etc.) as well as binary data, add other elements to the form element in which <input type="file" /> exists. If you submit the form element in which <input type="file" /> exists, the web browser will transmit data with a different protocol than when sending only string data. In this case, the getParameter(String name) method can not access data sent from the client. Most programmers use external libraries to access data sent with binary data and binary data.

String transfer example

Let's practice how to get data sent by the client in the servlet. Create SignUp.html in the documentbase/example diretory.

/example/SignUp.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
</head>
<body>
<h3>Sign Up</h3>

<form id="joinForm" action="../RegisterServlet" method="post">
<div>ID <input type="text" name="id" /></div>
<div>Nickname <input type="text" name="nickname" /></div>
<div>Password <input type="password" name="passwd" /></div>
<div>Name <input type="text" name="name" /></div>
<div>Gender Male <input type="radio" name="gender" value="M" /> Female <input type="radio" name="gender" value="F" /></div>
<div>Birthday <input type="text" name="birthday" /></div>
<div>Mobile <input type="text" name="mobile" /></div>
<div>Telephone <input type="text" name="tel" /></div>
<div>Address <input type="text" name="address" /></div>
<div>Email <input type="text" name="email" /></div>
<div>
Sports
<input type="checkbox" name="sports" value="soccer" />soccer
<input type="checkbox" name="sports" value="baseball" />baseball
<input type="checkbox" name="sports" value="basketball" />Basketball
<input type="checkbox" name="sports" value="tennis" />Tennis
<input type="checkbox" name="sports" value="tabletennis" />Tabletennis
</div>
<div>
Lectures
<select name="main-menu" multiple="multiple">
  <option value="">-- Multiple Select --</option>
  <option value="java">JAVA</option>
  <option value="jdbc">JDBC</option>
  <option value="jsp">JSP</option>
  <option value="css-layout">CSS Layout</option>
  <option value="jsp-prj">JSP Project</option>
  <option value="spring">Spring</option>
  <option value="javascript">JavaScript</option>
</select>
</div>
<div>
About Me
<textarea name="aboutMe" cols="40" rows="7"></textarea>
</div>
<div><input type="submit" value="Submit" /></div>
</form>
</body>
</html>

Create RegisterServlet.java in /WEB-INF/src/example directory.

RegisterServlet.java
package example;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class RegisterServlet extends HttpServlet {
  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp)
      throws IOException,ServletException {
		
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
    req.setCharacterEncoding("UTF-8");
		
    String id = req.getParameter("id");
		
    out.println("<html><body>");
    out.println("id : " + id);
		
    String[] sports = req.getParameterValues("sports");
    int len = sports.length;
		
    out.println("<ol>");
    for (int i = 0; i < len; i++) {
      out.println("<li>" + sports[i] + "</li>");
    }
		
    out.println("</ol>");
		
    String path = req.getContextPath();
    out.println("<a href=" + path + "/example/SignUp.html>Join</a>");
    out.println("</body></html>");
  }

}

Open a command prompt and go to /WEB-INF/src/example and compile as below.

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^
-cp C:/apache-tomcat-9.0.87/lib/servlet-api.jar ^
RegisterServlet.java

Add the following to web.xml:

web.xml
<servlet>			
  <servlet-name>RegisterServlet</servlet-name>
  <servlet-class>example.RegisterServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>RegisterServlet</servlet-name>
  <url-pattern>/RegisterServlet</url-pattern>
</servlet-mapping>

After restarting Tomcat, go to http://localhost:8080/example/SignUp.html. TODO: Add a code to the servlet to check for values other than ID and sports.

RequestDispatcher Interface

RequestDispathcer has two methods, include() and forward(). The include() passes control to another resource and retakes control when another resource completes its work. As a result, It adds messages produced by other resources to the response. The forward() passes control to another resource, which response to the client.

Let's practice the forward() method example. Create the ControllerServlet.java file in the /WEB-INF/src/example directory as follows:

ControllerServlet.java
package example;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ControllerServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doPost(req, resp);
  }

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setCharacterEncoding("UTF-8");
		
    String uri = req.getRequestURI();
    String contextPath = req.getContextPath();
    String command = null;
    String view = null;
    boolean isRedirect = false;
		
    command = uri.substring(contextPath.length());
		
    if (command.equals("/example/SignUp.action")) {
      view = "/example/SignUp.html";
    }
		
    if (isRedirect == false) {
      ServletContext sc = this.getServletContext();
      RequestDispatcher rd = sc.getRequestDispatcher(view);
      rd.forward(req, resp);
    } else {
      resp.sendRedirect(view);
    }
  }

}

Open a command prompt and go to /WEB-INF/src/example and compile as follows:

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^
-cp C:/apache-tomcat-9.0.87/lib/servlet-api.jar ^
ControllerServlet.java

Add the following to web.xml:

web.xml
<servlet>
  <servlet-name>Controller</servlet-name>
  <servlet-class>example.ControllerServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
  <servlet-name>Controller</servlet-name>
  <url-pattern>*.action</url-pattern>
</servlet-mapping>

After restarting Tomcat, go to http://localhost:8080/example/SignUp.action and verify that /example/SignUp.html responds.

The process until the doPost() method runs.

We added servlet declaration and servlet-mapping of ControllerServlet in web.xml so that ControllerServlet would handle all requests ending in .action. When a request string which ends in .action comes, Tomcat interprets the mapping information in web.xml and invokes ControllerServlet's +servlet(servletRequest req, ServletResponse res). The +service(ServletRequest req, ServletResponse res) simply calls the #service(HttpServletRequest req, HttpServletResponse resp). The #service (HttpServletRequest req, HttpServletResponse resp) is responsible for checking the HTTP METHOD of the request and calling the appropriate method according to HTTP METHOD. HTTP METHOD of the request is the GET method because you requested http://localhost:8080/example/SignUp.action in the address bar of your web browser. So, #service() calls doGet() method. The doGet() calls the doPost() as we implemented it.

The following summarizes the HttpServletRequest methods used in the body of doPost().

getRequestURI()
Returns the part of this request's URL from the protocol name to the query string in the HTTP request.
When requesting http://localhost:8080/example/SignUp.action in a web browser, it returns "/example/SignUp.action".
The string following the quotation mark (?) after the URL is called a query string. The query string passes to the server-side resource corresponding to the URL. If there is more than one information in the query string, use & from the second. (e.g., http://localhost:8080/list.jsp?board=chat&page=1)
getContextPath()
Returns the portion of the request URI that indicates the context of the request.
As we work in the ROOT application, the value we get through this method is "".

The example detects the URL requested by the user with the following code.

req.getRequestURI().substring(req.getContextPath().length()):

The code returns "/example/SignUp.action".

The user requesting /example/SignUp.action receives a response of /example/SignUp.html.
Therefore, the user requested /example/SignUp.action gets a response from /example/SignUp.html.

TODO

In ControllerServlet.java, change isRedirect to true and recompile and test.

Servlet using database

Let's convert the GetEmp.java file, which we practiced in the JDBC chapter, into a servlet.
This task describes how to turn a pure Java application into a servlet.
Create the GetEmpServlet.java file in the /WEB-INF/src/example directory of the ROOT application.

GetEmpServlet.java
package example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GetEmpServlet extends HttpServlet {
	
  private String DB_URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
  private String DB_USER = "scott";
  private String DB_PASSWORD = "tiger";
	
  /*
   * GenericServlet's init() method
   * init(ServletConfig config) invoke this method.
   * Therefore, you do not need to override the init(ServletConfig config) method.
   */
  @Override
  public void init() throws ServletException {
    try {
      Class.forName( "oracle.jdbc.driver.OracleDriver" );
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
	

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
		
    Connection con = null;
    Statement stmt = null;
    ResultSet rs = null;
		
    String sql = "select * from emp";
		
    try {
      con = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
      stmt = con.createStatement();
      rs = stmt.executeQuery(sql);
			
      while (rs.next()) {
        String empno = rs.getString(1);
        String ename = rs.getString(2);
        String job = rs.getString(3);
        String mgr = rs.getString(4);
        String hiredate = rs.getString(5);
        String sal = rs.getString(6);
        String comm = rs.getString(7);
        String depno = rs.getString(8);
			
        out.println( empno + " : " + ename + " : " + job + " : " + mgr + 
          " : " + hiredate + " : " + sal + " : " + comm+" : " + depno + "<br />" );
      }

    } catch (SQLException e) {
      e.printStackTrace(out);
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (stmt != null) {
        try {
          stmt.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
    }
  }

}

Use the command prompt to navigate to the /WEB-INF/src/example directory of the ROOT application and compile it as follows:

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^ 
-cp C:/apache-tomcat-9.0.87/lib/servlet-api.jar ^
GetEmpServlet.java

Add the following to web.xml:

web.xml
<servlet>
  <servlet-name>GetEmpServlet</servlet-name>
  <servlet-class>example.GetEmpServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>GetEmpServlet</servlet-name>
  <url-pattern>/empList</url-pattern>
</servlet-mapping>

Copy the JDBC driver file to CATALINA_HOME/lib.
After restarting Tomcat, visit http://localhost:8080/empList.

7369 : SMITH : CLERK : 7902 : 1980-12-17 00:00:00.0 : 800 : null : 20
7499 : ALLEN : SALESMAN : 7698 : 1981-02-20 00:00:00.0 : 1600 : 300 : 30
7521 : WARD : SALESMAN : 7698 : 1981-02-22 00:00:00.0 : 1250 : 500 : 30
7566 : JONES : MANAGER : 7839 : 1981-04-02 00:00:00.0 : 2975 : null : 20
7654 : MARTIN : SALESMAN : 7698 : 1981-09-28 00:00:00.0 : 1250 : 1400 : 30
7698 : BLAKE : MANAGER : 7839 : 1981-05-01 00:00:00.0 : 2850 : null : 30
7782 : CLARK : MANAGER : 7839 : 1981-06-09 00:00:00.0 : 2450 : null : 10
7788 : SCOTT : ANALYST : 7566 : 1987-04-19 00:00:00.0 : 3000 : null : 20
7839 : KING : PRESIDENT : null : 1981-11-17 00:00:00.0 : 5000 : null : 10
7844 : TURNER : SALESMAN : 7698 : 1981-09-08 00:00:00.0 : 1500 : 0 : 30
7876 : ADAMS : CLERK : 7788 : 1987-05-23 00:00:00.0 : 1100 : null : 20
7900 : JAMES : CLERK : 7698 : 1981-12-03 00:00:00.0 : 950 : null : 30
7902 : FORD : ANALYST : 7566 : 1981-12-03 00:00:00.0 : 3000 : null : 20
7934 : MILLER : CLERK : 7782 : 1982-01-23 00:00:00.0 : 1300 : null : 10

If you do not see the results you want, check the list below.

  • Is the servlet declaration and servlet mapping of GetEmpServlet added correctly to the web.xml file?
  • Is there GetEmpServlet bytecode in the /WEB-INF/classes/example directory.
  • Is there an Oracle JDBC driver file (ojdbc6.jar) in the CATALINA_HOME/lib directory.
  • Did the ROOT web application load successfully?

Using ServletConfig initialization parameter

Let's modify the GetEmpServlet example to use the ServletConfig initialization parameter.
Create the following servlet in the /WEB-INF/src/example directory.

InitParamServlet.java
package example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class InitParamServlet extends HttpServlet {
	
  private String url;
  private String user;
  private String passwd;
  private String driver;
	
  @Override
  public void init() throws ServletException {
    url = this.getInitParameter("url");
    user = this.getInitParameter("user");
    passwd = this.getInitParameter("passwd");
    driver = this.getInitParameter("driver");
		
    try {
      Class.forName(driver);
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
	
  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
		
    Connection con = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
		
    String sql = "SELECT * FROM emp";
		
    try {
      con = DriverManager.getConnection(url, user, passwd);
      stmt = con.prepareStatement(sql);
      rs = stmt.executeQuery();
			
      while (rs.next()) {
        String empno = rs.getString(1);
        String ename = rs.getString(2);
        String job = rs.getString(3);
        String mgr = rs.getString(4);
        String hiredate = rs.getString(5);
        String sal = rs.getString(6);
        String comm = rs.getString(7);
        String depno = rs.getString(8);
				
        out.println(empno + " : " + ename + " : " + job + " : " + mgr + 
            " : " + hiredate + " : " + sal + " : " + comm+" : " + depno + "<br />");
      }
    } catch (SQLException e) {
      e.printStackTrace(out);
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (stmt != null) {
        try {
          stmt.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
    }
  }

}

Add the following to web.xml:

web.xml
<servlet>
  <servlet-name>InitParamServlet</servlet-name>
  <servlet-class>example.InitParamServlet</servlet-class>
  <init-param>
    <param-name>driver</param-name>
    <param-value>oracle.jdbc.driver.OracleDriver</param-value>
  </init-param>
  <init-param>
    <param-name>url</param-name>
    <param-value>jdbc:oracle:thin:@127.0.0.1:1521:XE</param-value>
  </init-param>
  <init-param>
    <param-name>user</param-name>
    <param-value>scott</param-value>
  </init-param>
  <init-param>
    <param-name>passwd</param-name>
    <param-value>tiger</param-value>
  </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>InitParamServlet</servlet-name>
  <url-pattern>/initParam</url-pattern>
</servlet-mapping>

You can obtain values of the initialization parameters with the getInitParameter(String name) method of ServletConfig.
After restarting Tomcat, visit http://localhost:8080/initParam.

Using ServletContext Initialization Parameters

All servlets and JSPs can reference the ServletContext initialization parameters in the web application. The ServletContext initialization parameter is set in web.xml using the context-param element. The elements in web.xml have a specified order. The following excerpts from http://java.sun.com/dtd/web-app_2_3.dtd.

<!ELEMENT web-app (icon?, display-name?, description?, distributable?, context-param*, filter*, filter-mapping*, listener*, servlet*, servlet-mapping*, session-config?, mime-mapping*, welcome-file-list?, error-page*, taglib*, resource-env-ref*, resource-ref*, security-constraint*, login-config?, security-role*, env-entry*, ejb-ref*, ejb-local-ref*)>

You can see that the context-param must be declared ahead of the servlet.
Open web.xml and add the following:

web.xml
<context-param>
  <param-name>url</param-name>
  <param-value>jdbc:oracle:thin:@127.0.0.1:1521:XE</param-value>
</context-param>

The getServletContext() method return a reference of ServletContext instance.
The value of url, which is the ServletContext initialization parameter declared above, is obtained by using getInitParameter(String name) method of ServletContext.
Add the following code to the SimpleServlet.java and recompile.

ServletContext sc = getServletContext();
String url = sc.getInitParameter("url");
out.println(url);

After restarting Tomcat, visit http://localhost:8080/simple.

Listener

The listener runs on the web application event. Two types of Web application events:

  • Application startup and shutdown
  • Session creation and session invalidation

An application startup event occurs when a web application is first loaded and started by a servlet container.
Application shutdown events occur when the web application is shut down.
Session creation events occur when the servlet container creates a new session.
Session invalidation events occur each time a session is invalidated.
To use events, you need to write a class called Listener.
The Listener class is a pure Java class that implements the following interfaces:

  • javax.servlet.ServletContextListener
  • javax.servlet.http.HttpSessionListener

If you want listeners for application startup or shutdown events, implement ServletContextListener interface.
ServletContextListener interface consists of two methods:

  • public void contextInitialized(ServletContextEvent sce);
  • public void contextDestroyed(ServletContextEvent sce);

If you want listeners for session creation and session invalidation events, implement HttpSessionListener interface.
HttpSessionListener interface consists of two methods:

  • public void sessionCreated(HttpSessionEvent se);
  • public void sessionDestroyed(HttpSessionEvent se);

Write the following Listener class, which, right after the web application startup, creates an OracleConnectionManager object and stores it in the ServletContext.

MyServletContextListener.java
package net.java_school.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import net.java_school.db.dbpool.OracleConnectionManager;

public class MyServletContextListener implements ServletContextListener {

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    ServletContext sc = sce.getServletContext();
    OracleConnectionManager dbmgr = new OracleConnectionManager();
    sc.setAttribute("dbmgr", dbmgr);
  }

  @Override
  public void contextDestroyed(ServletContextEvent sce) {
    ServletContext sc = sce.getServletContext();
    sc.removeAttribute("dbmgr");
  }

}

Add the following to web.xml, below the context-param, above the servlet declarations.

web.xml
<listener>
  <listener-class>net.java_school.listener.MyServletContextListener</listener-class>
</listener>

Using a custom connection pool

Add all the java source files from the lession entitled "ConnectionPool" in the JDBC chapter to the ROOT application's WEB-INF/src directory.
Open Log.java and modify it as follows:

public String logFile = "C:/www/myapp/WEB-INF/myapp.log";

Copy the orcale.properties file in the "ConnectionPool" to the ROOT application's WEB-INF directory.
Open ConnectionManager.java and modify it as follows:

configFile = "C:/www/myapp/WEB-INF/" + poolName + ".properties";

When the ROOT web application starts, out Listener will create an OracleConnectionManager object and store its reference in the ServletContext.
To test it, modify the GetEmpServlet.java file as shown below.

GetEmpServlet.java
package example;

import java.sql.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

import net.java_school.db.dbpool.*;

public class GetEmpServlet extends HttpServlet {

  private OracleConnectionManager dbmgr;
	
  @Override
  public void init() throws ServletException {
    ServletContext sc = getServletContext();
    dbmgr = (OracleConnectionManager) sc.getAttribute("dbmgr");
  }
	
  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
			
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
		
    Connection con = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
		
    String sql = "SELECT * FROM emp";
		
    try {
      con = dbmgr.getConnection();
      stmt = con.prepareStatement(sql);
      rs = stmt.executeQuery();
			
      while (rs.next()) {
        String empno = rs.getString(1);
        String ename = rs.getString(2);
        String job = rs.getString(3);
        String mgr = rs.getString(4);
        String hiredate = rs.getString(5);
        String sal = rs.getString(6);
        String comm = rs.getString(7);
        String depno = rs.getString(8);
				
        out.println( empno + " : " + ename + " : " + job + " : " + mgr +
          " : " + hiredate + " : " + sal + " : " + comm+" : " + depno + "<br>" );
      }
    } catch (SQLException e) {
      e.printStackTrace(out);
    } finally {
      if (rs != null) {
        try {
          rs.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (stmt != null) {
        try {
          stmt.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
      if (con != null) {
        try {
          con.close();
        } catch (SQLException e) {
          e.printStackTrace();
        }
      }
    }
  }

}

Compile the custom connection pool source and GetEmpServlet.java.
Restart Tomcat and visit http://localhost:8080/empList. (We have already set up the declaration and mapping for the GetEmpServlet in previous exercises)

The HttpSessionListener interface consists of two methods. One for session creation events and the other for session invalidation events.

  • public void sessionCreated(HttpSessionEvent se);
  • public void sessionDestroyed(HttpSessionEvent se);

The following is an example of an HttpSessionListener.

SessionCounterListener.java
package net.java_school.listener;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class SessionCounterListener implements HttpSessionListener {

  public static int totalCount;
	
  @Override
  public void sessionCreated(HttpSessionEvent event) {
    totalCount++;
    System.out.println("Session Increase. Total Sessions:" + totalCount);
  }

  @Override
  public void sessionDestroyed(HttpSessionEvent event) {
    totalCount--;.
    System.out.println("Session Reduction. Total Sessions:" + totalCount);
  }

}

Add the following to web.xml:

<listener>
  <listener-class>net.java_school.listener.SessionCounterListener</listener-class>
</listener>

Restart Tomcat and visit http://localhost:8080/simple.
Visit http://localhost:8080/simple with another web browser.
Check log messages in the Tomcat log file.

Filter

If there is code to execute before a user's request reaches a server resource, use filter.
For the filter to work, you need to add declaration and mapping about the filter to web.xml.
If filter one followed by filter two in web.xml in order, filter one - filter two - Server resource - filter two - filter one runs.
A filter class must implement the following interface:

javax.servlet.filter

The following is a pure Java application created to understand the filter mechanism.

ChainFilter.java
package net.java_school.filter;

import java.util.ArrayList;
import java.util.Iterator;

public class ChainFilter {
  private ArrayList<Filter> filters;
  private Iterator<Filter> iterator;

  public void doFilter() {
    if (iterator.hasNext()) {
      iterator.next().doFilter(this);
    } else {
      System.out.println("Run Server resource");
    }
  }

  public ArrayList<Filter> getFilters() {
    return filters;
  }

  public void setFilters(ArrayList<Filter> filters) {
    this.filters = filters;
    this.iterator = filters.iterator();
  }
	
}
Filter.java
package net.java_school.filter;

public interface Filter {
	
  public void doFilter(ChainFilter chain);

}
Filter1.java
package net.java_school.filter;

public class Filter1 implements Filter {

  @Override
  public void doFilter(ChainFilter chain) {
    System.out.println("Run Filter 1 before the server resource runs");
    chain.doFilter();
    System.out.println("Run Filter 1 after the server resource runs");
  }

}
Filter2.java
package net.java_school.filter;

public class Filter2 implements Filter {

  @Override
  public void doFilter(ChainFilter chain) {
    System.out.println("Run Filter 2 before the server resource runs");
    chain.doFilter();
    System.out.println("Run Filter 2 after the server resource runs");
  }

}
Tomcat.java
package net.java_school.filter;

import java.util.ArrayList;

public class Tomcat {

  public static void main(String[] args) {
    ChainFilter chain = new ChainFilter();
    ArrayList<Filter> filters = new ArrayList<Filter>();
    Filter f1 = new Filter1();
    Filter f2 = new Filter2();
    filters.add(f1);
    filters.add(f2);
    chain.setFilters(filters);
    chain.doFilter();
  }

}
Run Filter 1 before the server resource runs
Run Filter 2 before the server resource runs
Run Server resource.
Run Filter 2 after the server resource runs
Run Filter 1 after the server resource runs

Filter Interface

  • init(FilterConfig filterConfig) throws ServletException; When the servlet container calls this method, the filter is in service state.
  • doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; Called by the servlet container to perform the filtering operation.
  • destroy() When called by the servlet container, the filter is no longer able to service. Mainly it used to return resources.

The doFilter() method's arguments show that the filter can access the ServletRequest, ServletResponse, and javax.servlet.FilterChain objects when it intercepts the request or response. A FilterChain has a list of filters to run in order.

In the doFilter() method, the code before FilterChain's doFilter() is the code to run before the request reaches the server resource, and the code after FilterChain's doFilter() is the code to run before the response.

Filter Example

The following is an example that runs req.setCharacterEncoding("UTF-8"); code before all server resources are executed.
Create a CharsetFilter.java file as follows:

/WEB-INF/src/net/java_school/filter/CharsetFilter.java
package net.java_school.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class CharsetFilter implements Filter {

  private String charset = null;
	
  @Override
  public void init(FilterConfig config) throws ServletException {
    this.charset = config.getInitParameter("charset");
  }
	
  @Override
  public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
    throws IOException, ServletException {
		
    if (req.getCharacterEncoding() == null) {
      req.setCharacterEncoding(charset);
      chain.doFilter(req,resp);
    }
  }

  @Override
  public void destroy() {
    //Implement it if you have resources to return.
  }

}

At the command prompt, go to the /WEB-INF/src/net/java_school/filter directory and compile as follows.

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^
-cp C:/apache-tomcat-9.0.87/lib/servlet-api.jar ^
CharsetFilter.java

Open the web.xml file and add the following:

web.xml
<filter>
  <filter-name>CharsetFilter</filter-name>
  <filter-class>net.java_school.filter.CharsetFilter</filter-class>
  <init-param>
     <param-name>charset</param-name>
     <param-value>UTF-8</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>CharsetFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

You have to place the above code between the context-param element and the listener element-

Open the RegisterServlet.java source and comment out the req.setCharacterEncoding("UTF-8"); and recompile RegisterServlet.java.
Go to http://localhost:8080/example/SignUp.html and input the non-English characters into the ID and click submit button. If req.setCharacterEncoding("UTF-8"); is executed, the ID will be output correctly.

You can set up filter initialization parameters using the filter's subelement, init-param.
The getInitParameter() and getInitParameters() methods of FilterConfig are used to read filter initialization parameters.

The filter-mapping element in web.xml specifies the resource to be filtered.
The servlet container adds filters to the FilterChain in the order they are listed in web.xml.
But the filter mapped to the servlet name is added after the filter that matches the URL pattern.
The doFilter() method of FilterChain calls the next filter of FilterChain.

Uploading files

This section introduces two external libraries to upload files.

MultipartRequest

Download the latest file from the following address:
http://www.servlets.com/cos/index.html
After decompressing, copy the cos.jar file to the ROOT application's /WEB-INF/lib directory.
There are eight constructors for the MultipartRequest class. For a more detailed explanation, please refer to the following address:
http://www.servlets.com/cos/javadoc/com/oreilly/servlet/MultipartRequest.html
The following constructor resolves the encoding problem for non-English characters and automatically rename the file name when its name is the same as a file of the server file system.

public MultipartRequest(HttpServletRequest request,
  String saveDirectory,
  int maxPostSize,
  String encoding,
  FileRenamePolicy policy) throws IOException

Methods of MultipartRequest

If the MultipartRequest constructor has done successfully, the upload was successful.
The following shows the methods of MultipartRequest available after uploading files to the server's file system.
Suppose you upload the logo.gif image file using the <input type="file" name="photo" />.

getContentType("photo");
Return the MIME type of the uploaded file. For example, if the file's extension is a gif, "image/gif" is returned.
getFile("photo");
Return the File object of the file uploaded and stored in the server.
getFileNames();
Return parameter names whose type attribute is "file" as Enumeration type.
getFilesystemName("photo");
Returns the actual file name that is uploaded and exists in the server file system.
getOriginalFileName("photo");
Return original filename
Methods to provide the same interface as HttpServletRequest
getParameter(String name);
Return the value of parameter with the given name.
getParameterNames();
Return all parameter names as Enumeration type.
getParameterValues(String name);
Returns values of the parameter with the given name.

MultipartRequest Example

Create the following HTML file in the DocumentBase/example directory of the ROOT application.

MultipartRequest.html
<!DOCTYPE html>
<html>
<head>
  <title>MultipartRequest Servlet Example</title>
  <meta charset="UTF-8" />
</head>
<body>

<h1>File Upload Test with MultipartRequest</h1>

<form action="../servlet/UploadTest" method="post" enctype="multipart/form-data">
<div>Name: <input type="text" name="name" /></div>
<div>File 1: <input type="file" name="file1" /></div>
<div>File 2: <input type="file" name="file2" /></div>
<div><input type="submit" value="Submit" /></div>
</form>

</body>
</html>

Create the following servlet in the WEB-INF/src/example directory.

UploadTest.java
package example;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

public class UploadTest extends HttpServlet {
	
  @Override	
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
			
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
		
    ServletContext cxt = getServletContext();
    String dir = cxt.getRealPath("/upload");
		
    try {
      MultipartRequest multi = new MultipartRequest(req, dir, 5*1024*1024, "UTF-8", new DefaultFileRenamePolicy());
					
      out.println("<html>");
      out.println("<body>");
      out.println("<h1>Parameters passed by the user</h1>");
      out.println("<ol>");
      Enumeration<?> params = multi.getParameterNames();
			
      while (params.hasMoreElements()) {
        String name = (String) params.nextElement();
        String value = multi.getParameter(name);
        out.println("<li>" + name + "=" + value + "</li>");
      }
			
      out.println("</ol>");
      out.println("<h1>Uploaded files</h1>");
			
      Enumeration<?> files = multi.getFileNames();
			
      while (files.hasMoreElements()) {
        out.println("<ul>");
        String name = (String) files.nextElement();
        String filename = multi.getFilesystemName(name);
        String originalName =multi.getOriginalFileName(name);
        String type = multi.getContentType(name);
        File f = multi.getFile(name);
        out.println("<li>Parameter name: "  + name + "</li>");
        out.println("<li>File name: " + filename + "</li>");
        out.println("<li>File original name: " + originalName + "</li>");
        out.println("<li>File type: " + type + "</li>");
				
        if (f != null) {
          out.println("<li>File size: " + f.length() + "</li>");
        }
        out.println("</ul>");
      }
    } catch(Exception e) {
      out.println("<ul>");
      e.printStackTrace(out);
      out.println("</ul>");
    }
    out.println("</body></html>");
  }

}

When compiling, you need to tell the Java compiler the path to the cos.jar file.

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^
-cp C:/www/myapp/WEB-INF/lib/cos.jar ^
UploadTest.java

Add the following to web.xml:

web.xml
<servlet>
  <servlet-name>UploadTest</servlet-name>
  <servlet-class>example.UploadTest</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>UploadTest</servlet-name>
  <url-pattern>/servlet/UploadTest</url-pattern>
</servlet-mapping>

TEST

  1. Create a subdirectory named upload in the DocumentBase of the ROOT application.
  2. Restart Tomcat and visit http://localhost:8080/example/upload.html.
  3. After uploading duplicate files, check the file name in upload folder. If you upload the same name file, you will see a number appended to the file name's end.

If the test fails, check the list below.

  1. Check that the bytecode of the UploadTest servlet exists.
  2. Make sure that there is a subdirectory named upload in the DocumentBase of the ROOT application.
  3. Make sure the cos.jar file is in WEB-INF/lib in the ROOT application.
  4. Make sure the UploadTest servlet is correctly registered and mapped in the web.xml.
  5. Make sure the ROOT web application is loaded.

commons-fileupload

Commons-fileupload is a file upload library provided by The Apache Software Foundation.
Download the latest binary files from the following addresses:

Uncompress and copy the commons-fileupload-<version>.jar and commons-io-<version>.jar files to the ROOT application's /WEB-INF/lib.

Example

Create the following HTML file in the DocumentBase/example directory of the ROOT application.

commons-fileupload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>commons-fileupload Test</title>
</head>
<body>

<h1>Uploading files using commons-fileupload</h1>

<form action="../CommonsUpload" method="post" enctype="multipart/form-data">
<div>File : <input type="file" name="upload" /></div>
<div><input type="submit" value="Submit" /></div>
</form>

</body>
</html>

Create the following servlet in the WEB-INF/src/example directory of the ROOT application.

CommonsUpload.java
package example;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import java.util.Iterator;
import java.io.File;
import java.util.List;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class CommonsUpload extends HttpServlet {

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) 
      throws IOException, ServletException {
			
    resp.setContentType("text/html; charset=UTF-8");
    PrintWriter out = resp.getWriter();
    //Check that we have a file upload request
    boolean isMultipart = ServletFileUpload.isMultipartContent(req);
    //Create a factory for disk-based file items
    DiskFileItemFactory factory = new DiskFileItemFactory();
		
    //Configure a repository (to ensure a secure temp location is used)
    ServletContext servletContext = this.getServletConfig().getServletContext();
    File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
    factory.setRepository(repository);
		
    //Create a new file upload handler
    ServletFileUpload upload = new ServletFileUpload(factory);
    upload.setHeaderEncoding("UTF-8");
    try {
      //Parse the request
      List<FileItem> items = upload.parseRequest(req);
      //Process a file upload
      Iterator<FileItem> iter = items.iterator();
      while (iter.hasNext()) {
        FileItem item = iter.next();
        String fileName = null;
        if (!item.isFormField()) {
          String fieldName = item.getFieldName();
          out.println(fieldName);
          fileName = item.getName();
          out.println(fileName);
          String contentType = item.getContentType();
          out.println(contentType);
          boolean isInMemory = item.isInMemory();
          out.println(isInMemory);
          long sizeInBytes = item.getSize();
          out.println(sizeInBytes);
        }
        // Process a file upload
        ServletContext cxt = getServletContext();
        String dir = cxt.getRealPath("/upload");
        File uploadedFile = new File(dir + "/" + fileName);
        item.write(uploadedFile);
      }
    } catch (Exception e) {
      out.println("<ul>");
      e.printStackTrace(out);
      out.println("</ul>");
    }
    out.println("<a href="/example/commons-fileupload.html">commons-fileupload.html</a>");
  }

}

Compile it like below.

C:\ Command Prompt
javac -d C:/www/myapp/WEB-INF/classes ^
-cp C:/www/myapp/WEB-INF/lib/commons-fileupload-<version>.jar; ^
C:/www/myapp/WEB-INF/lib/commons-io-<version>.jar ^
CommonsUpload.java

Add the following to web.xml:

web.xml
<servlet>
  <servlet-name>commonsUpload</servlet-name>
  <servlet-class>example.CommonsUpload</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>commonsUpload</servlet-name>
  <url-pattern>/CommonsUpload</url-pattern>
</servlet-mapping>

Restart Tomcat and visit http://localhost:8080/example/commons-fileupload.html.
When uploading the same name file, unlike cos.jar, it overwrites the existing file.

Cookie

The HTTP protocol does not maintain a state. A cookie is one of technologies for overcoming a web browser's inabilty to hold a communication session with a server due to the HTTP protocol's characteristics.

The server sends a cookie to the web browser.

First, the server makes and sends the cookie to a web browser. The cookie looks like as follows:

Set-Cookie : name = value ; expires = date ; path = path ; domain = domain ; secure

A web browser stores the cookie passed from the server into a file or memory.

The web browser sends the cookie to the server.

Once the web browser stores the cookie, the web browser sends the cookie whenever it requests the server resource. The cookie looks like the following:

Cookie ; name = value1 ; name2 = value2 ;

Cookie name and value can not contain characters such as [] () = "/? @ :;

Cookie's constructor

Creates a cookie using the Cookie(String name, String value) constructor.

Methods of Cookie class

setValue(String value)
Reset the value of the created cookie.
setDomain(String pattern)
By default, a web browser can send cookies the server that sent them. If you want to send a cookies to another server that belongs to the same domain group, use the setDomain() method. But you cannot specify domains not related to the server with setDomain().
setMaxAge(int expiry)
Set the validity period of the cookie in seconds. If you set a negative number, a web browser deletes the cookie when it terminates.
setPath(String uri)
Sets the path to which cookies will be applied. If you specify a Uri, a web browser will send the cookie to the web server only if it visit the Uri.
setSecure(boolean flag)
If the flag is true, cookies are sent only for servers using secure channels. (https)

If you create a cookie with the cookie constructor and call the cookie's methods to set the cookie appropriately, you need to send it to a web browser. The following is the code that sends a cookie to a web browser.

resp.addCookie(cookie);

Access cookies sent by web browser

Cookie[] cookie = req.getCookies();

Use the getCookies() method of HttpServletRequest to get the cookie array. If there is no cookie, the getCookies() method returns null. You can get information about cookies by using the following method. Among them, getName() and getValue() are mainly used.

getName()
Return cookie name
getValue()
Return cookie value
getDomain()
Return cookie domain
getMaxAge()
Return cookie expiration

Here is the code snippet that gets cookie value on the server resource.

String id = null;
Cookie[] cookies = request.getCookies();

if (cookies != null) {

  for (int i = 0; i < cookies.length; i++) {
    String name = cookies[i].getName();
    if (name.equals("id")) {
      id = cookies[i].getValue();
      break;
    }
  }

}

The following code shows how to delete a cookie. It creates a cookie with the same name as the cookie you want to delete, calls setMaxAge(0), and sends the cookie to a web browser.

Cookie cookie = new Cookie("id","");
cookie.setMaxAge(0);
resp.addCookie(cookie);

The JSP chapter covers cookie examples.

Session

A session is a cookie-based technology for overcoming weaknesses in cookie security. Unlike cookies, a web browser stores only session ID as a cookie. Session ID cookie stored in a web browser and session object (HttpSession Type) mapped to session ID cookie in server cooperate to maintain the user's data. The code snippet for creating a session is as follows.

HttpSession session = req.getSession(true); //If there is no session, create it.
HttpSession session = req.getSession(false); //If there is no session, returns null.

Once you create a session object, you can store the user's data in the session.

User user = new User("Alison", "11111111");
session.setAttribue("user", user);

The JSP chapter covers session examples.

References