Java Servlet and JSP

fwang2@ornl.gov

My study notes based on wonderful O’reilly Head First Servlet book.


Table of Contents:


1. Web Basics

1.1. HTTP GET Request

GET request append form data to the end of the URL. An Example:

GET /select/test.jsp?color=dark&shape=circle HTTP/1.1
Host: ...
Accept-Encoding: gzip, deflate
...
Connection: keep-alive

We need to know:

1.2. HTTP POST Request: Sending and Using Two Parameters

We need to know:

1.3. HTTP Response

HTTP/1.1 200 OK
Content-Type: text/html
...
<html> ....
</html>

The content-type response header is known as MIME type - it tells to browser what kind of data it is about to get so browser knows how to render it.

Common response/status code:

1.4. An Servlet Example

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

public class TestServ extends HttpServlet {
    public void doGet(HttpServlet request, HttpServletResponse response)
        throws IOException {
        PrintWriter out = response.getWriter();
        java.util.Date today = new java.util.Date();
        out.println("<html><body>" + today + "</body></html>");
    }
}

// compile
% javac -classpath $CATALINA_HOME/common/lib/servlet-api.jar \
    -d classes src/TestServ.java

// deploy

As you can see, a servlet has doGet and doPost to handle GET request and POST request, it doesn’t have a main() method, that is where the container comes in.

2. Servlet and Container

2.1. What is container?

When a request for a servlet comes in, the server (such as Apache) hands the request not to servlet itself, but to the Container in which the servlet is deployed. It is the container that gives the servlet the HTTP request and response, and it is the container that calls servlet’s method such as doGet() and doPost().

2.2. Why Container?

2.3. How does container work?

2.4. Servlet name and Deployment Descriptor (DD) mapping

One key function of container is to locate corresponding servlet. A servlet is known to have three names:

The mapping can be accomplished by two DD elements:

Note /public_whatever is what client sees and uses to get to the servlet.

2.5. Tomcat is a web container, not a J2EE application server.

3. Servlet Lifecycle and API

3.1. Servlet’s life

3.2. Three Life cycle moments

3.3. ServletConfig object

3.4. ServletContext object

3.5. Idempotent (or Not)

3.6. Example: servlet code to download a JAR

public void doGet(HttpServletRequest request, HttpServletResponse
    response) throws IOException, ServletException {

    // let browser know this is a jar, not html
    response.getContentType("application/jar");

    ServeletContext ctx = getServletContext();
    InputStream is = ctx.getResourceAsStream("/myCode.jar");
    int read = 0; 
    byte[] bytes = new byte[1024];

    OutputStream os = response.getOutputStream();
    while ((read = is.read(bytes) != -1) {
        os.write(bytes, 0, read);
    }
    os.flush(); os.close();
}

The getResourceAsStream() requires forward slash, which is the root of web app. That is

webapps/ --> JarDownload/ --> myCode.jar
                          --> WEB-INF/

3.7. Request Redirect

3.8. Redirect vs. Request Dispatch

The biggest difference is that “redirect” makes the client do the work, while “request dispatch” make something else on the server do the work.

3.9. Summary

4. Web App: Attributes and Listeners

4.1. Servlet Init Parameters

    # web.xml
    <servlet>
        <servlet-name> ... </servlet-name>
        <servlet-class> ... </servlet-class>
        <init-param>
            <param-name>adminEmail</param-name>
            <param-value> abc@google.com</param-value>
        </init-param>
    </servlet>

In the servlet code:

out.println(getServletConfig().getInitParameter("adminEmail"));

A few caveats:

4.2. Context Init Parameters

If you want all other parts of your web app have access to that email address, you can put it as part of context parameter.

    # web.xml
    <servlet>
            ...
    </servlet>

    <context-param>
             <param-name>adminEmail</param-name>
             <param-value> abc@google.com</param-value>
    </context-param>

Note that we are not nesting context-param inside servlet anymore, it is for the whole web app. In the servlet code:

out.println(getServletContext().getInitParameter("adminEmail"));

4.3. Context Listener and why?

Context parameter can’t be anything except Strings. What if you want the whole web app have access to a shared database connection? You can’t put that code in a servlet, since then you must have ways to guarantee the servlet must run before anybody else.

What in need is a listener for a context initialization event, so it can run some code before the rest of the app can service a request.

We need three classes and one definition for doing this:

4.4. Other type of Listeners

Besides context event, you can listen for events related to context attributes, servlet requests and attributes, and HTTP session and session attributes. (P182)

A plain old HttpSessionAttributeLisener is just a class that wants to know when any type of attribute has been added, removed, or replaced in a session. But the HttpSessionBindingListener exists so taht the attribute itself can find out when it has been added or removed from a session. Example:

public class Dog implements HttpSessionBindingListener {
    ...
    public void ValueBound(HttpSessionBindingEvent event) {
    ...
    }

    public void ValueUnbound(HttpSessionBindingEvent event) {
    ...
    }
}

4.5. Attribute, Scope and API.

A few caveats:

4.6. More on RequestDispatcher

There are two ways you can get this object:

5. Session Management

A session is keep client-specific state across multiple requests.

5.1. How does Session work?

5.2. Session ID

HTTP is stateless, so as far as the Container’s concerned, each request is from a new client. How will container recognize client X from client Y?

The idea is simple: on the client’s first request, the Container generates a unique session ID and gives it back to the client with the response. the client sends back the session ID with each subsequent request. The container sees the ID, finds the matching session, and assocaites the session with the request.

The session ID is exchanged through cookies:

5.3. If client doesn’t take cookies: URL Rewriting

5.4. Timeout/Invalidate Sessions

Three ways a session can die:

Cookie can be used for stuff other than session, the best thing about it is, user doesn’t have to be involved. And you can tell a cookie to stay alive even after the browser shuts down.

5.6. Final note on session

There is whole bunch of events related to session such as sesson created, destory, session attributes added, removed, replaced, session is passivated in one VM, activated on another etc. where you can attach listeners.

6. JSP Is Just a Servlet

6.1. JSP Basic

6.2. JSP Implicit objects

The container makes the following implicit object available to JSP when they do the servlet translation:

API                    Implicit object
---------------------------------------
JspWriter               out
HttpServletRequest      request
HttpServletResponse     response
HttpSession             session
ServletContext          application
ServletConfig           config
JspException            exception      // only available to error page
PageContext             pageContext    
Object                  page

“page” is a new fourth scope, “page-level”, and page-scoped attributes are stored in pageContext. It encapulates other implicit objects.

6.3. API for generated servlet

6.4. Lifecycle of JSP

6.5. From Servlet to JSP

// Servlet Controller

public doPost(...)
{ 
    String name = request.getParameter("userName");
    request.setAttribute("name", name);
    ...
    RequestDispatcher view = request.getRequestDispacher("/result.jsp");
    view.forward(request, response);
}

// JSP (view) 

<html><body>
<%= request.getAttribute("name");

6.6. Access attribute in a JSP

7. EL: Expression Language

When JSP wants to access an attribute, assuming the value is an object, here is what you do without EL:

We can get rid of scripting by using bean-related standard actions.

7.1. Bean actions

To treat it like an bean and use standard action

    <jsp:useBean  id="person" class="foo.Person" scope="request" />

    Person created by servlet: 
    <jsp:getProperty name="person" property="name" />

Here:

7.2. Talk directly to JSP without Servlet

You can send request parameters straight into a a bean, without scripting: The html code:

<form action="TestBean.jsp">
    name: <input type="text" name="userName">
    ID#: <input type="text" name="userID">
    <input type="submit">
</form>

Inside TestBean.jsp:

<jsp:useBean id="person" type="foo.Person" class="foo.Employee">
    <jsp:setProperty name="person" property="name" param="userName">

Note that param attribute: the param value comes from the name attribute of the form input field.

It can be simplified further, though I don’t know if that is a good idea, by following some conventions:

// abstract class foo.Person
String getName();
void setName();

// foo.Employee extends foo.Person
int getEmpID()
void setEmpID(int)

Now the HTML needs to be (basically BOTH parameters matches the property name of the bean):

<form ...>
    name: <input type="text" name="name">
    ID#$: <input type="text" name="empID">
    ...
</form>

Then we get to do this:

<jsp:useBean ... >
    <jsp:setProperty name="person" property="*" />

Note that only String and primitives are converted automatically.

7.3. JSP EL and Implicit Objects

With EL, the access to JavaBean is even easier:

This

<%= ((foo.Person) reqeust.getAttribute("person")).getDog().getName() %>

can be replaced by:

${person.dog.name}

EL’s first field can be either implicit object of the following

pageScope           // map of scope attributes
requestScope
sessionScope
applicationScope

param               // map of request parameter
paramValues     
header              // map of request header
headerValues
cookie
initParam           // map of context init parameters

pageContext  // of all implicit objects, only pageContext is NOT a map.
             // it is an actual reference to pageContext object.

OR it can be an attribute in page scope, request scope, session scope, application scope.

7.4. dot . operator and [] operator

You can use dot operator to access bean properties and map values. However, what if it (e.g. Person) is an array or list? Here are the rules:

For example, in servlet:

String[] favoriateMusic = ["Dan", "Metal", "Foo bar"];
request.setAttribute("musicList", favoriteMusic);
reqeust.setAttribute("secret", 2);

The in JSP

First song is: ${musicList[0]}          // Dan
Second song is: ${musicList["1"]        // Meta  (coerced)
Third song is ${musicList[secret]}      // evaluted as ${musicList[2]}

7.5. More EL examples on request parameter, and cookies

7.6. The requestScope is NOT request object

The implicit requestScope is just a Map of the request scope attributes, not the request object itself. Say you want to know the HTTP method, which is a property of the request object, not an attribute at the request scope, you need to go through pageContext:

Method is: ${pageContext.request.method}.

The above code works because pageContext has a request property, and request has a method property.

The same can be said for other Map scope objects: for example, applicationScope IS NOT a reference to ServletContext, it is just where the application-scope attrbutes are bound.

7.7. EL functions

I am skipping this, see an example on Page 389.

7.8. EL operators

7.9. Other JSP standard actions

7.10. EL Summary

8. Using JSTL

JSP standard tag library, also known as custom tags.

8.1. Loop: <c:forEach>

Servlet code:

String[] movieList = ["Rush hour", "Return of the King"];
request.setAttribute("movieList", movieList);
...

JSP code:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...    
<table>
    <c:forEach var="movie" items="${movieList}" varStatus="movieLoopCount" >
        <tr> <td> Count: ${movieLoopCount.count} </td></tr>
        <tr> <td> ${movie} </td></tr>
    </c:forEach>
</table>

The key feature is that the tag assign each element in the collection to the variable you declare with the var attribute. The LoopTagStatus class has a count property that gives you the current value of the iteration.

8.2. Conditional: <c:if>

An example: if user is a member, you want to allow them to comment, by including a comment box:

<c: if test="${userType == 'member' }" >
    <jsp:include page="inputComments.jsp" />
</c:if>

Assuming a servlet somewhere set the userType attribute based on the user’s login information.

8.3. Switch: <c:choose>

For simple case of if else, JSTL has a tedious setup:

<c:choose>
    <c:when test="${userPref == 'performance' }">
        ...
    </c:when>

    <c:when test="${userPref == 'safety' }">
        ...
    </c:when>

    ...

    <c:otherwise>
        ...
    </c:otherwise>
</c:choose>

8.4. Set value by <c:set>, Remove by <c:remove>

In session management, we mention that if client doesn’t take cookie, servlet has to encode it to do URL rewriting. So if we have to do it in JSP, here is how it is done:

<a href="<c:url value='/inputComments.jsp' />"> Click here </a>

<c:url> does URL rewritting, but not URL encoding (deal with white space character for example). To do both, we need:

<c:url value="/inputComments.jsp" var="inputURL">
    <c:param name="firstName" value="${first}" />
    <c:param name="lastName" value="${last}" />
</c:url>

Assuming that:

<c:set var="last" value="Hidden Cursor" />
<c:set var="first" value="Crouching Pixels" />

Then, you can see the new URL with

${inputURL} 

The output should be:

/myApp/inputComments.jsp?firstName=Crouching+Pixels&lastName=Hidden+Cursor

9. MVC example (Plain JSP)

9.1. HTML form

<form method="POST" action="SelectBeer.do" >

<select name="color" size="1">
    <option> light
    <option> amber
    <option> brown
    <option> dark
</select>
...
<input type="SUBMIT">
</form>

9.2. result.jsp

<%@ page import="java.util.*" %>
<html><body>
...
<% 
    List styles = (List) request.getAttribute("styles");
    Iterator it = styles.iterator();
    while (it.hasNext()) {
        out.print("<br>try: " + it.next());
    }
%>
</body></html>

9.3. servlet

public class BeerSelect extends HttpServlet {

    public void doPost(HttpServletRequest request,
        HttpServletResponse response) throws IOException, ServletException
        {

        String c = request.getParameter("color");
        BeerExpert be = new BeerExpert()
        List result = be.getBrand(c);

        // add attribute for JSP to use
        request.setAttribute("styles", result); 
        
        // initiate request dispatcher for JSP
        RequestDispatcher view =
        request.getRequestDispatcher("result.jsp");

        // use request dispatcher to tell container to 
        // call JSP, and pass in request and response.
        view.forward(reqeust, response);

     }
  }

10. Deployment

10.1. Configure Error Page

11. Appendix

11.1. Servlet interface

The first three are life cycle methods:

service(ServletRequest, ServletResponse);
init(ServletConfig);
destroy();
getServletConfig();
getServletInfo();

11.2. GenericServlet implements Servlet interface

An abstract class that implements most of the basic servlet methods you need, but you rarely extend this class.

service(ServletRequest, ServletResponse);
init(ServletConfig);
destroy();
...
getInitParameter(String);
getInitParameterNames();
getServletContext();

11.3. HttpServlet extends GenericServlet

Also an abstract class, but reflect the HTTP-ness of the servlet. Notice that service() method doesn’t just take any servlet request, it is a HTTP-specific request and response.

service(HttpServletRequest, HttpServletResponse);
doGet(HttpServletRequest, HttpServletResponse);
doPost(HttpServletRequest, HttpServletResponse);
doHead(HttpServletRequest, HttpServletResponse);
doOptions(HttpServletRequest, HttpServletResponse);
doPut(HttpServletRequest, HttpServletResponse);
doTrace(HttpServletRequest, HttpServletResponse);
doDelete(HttpServletRequest, HttpServletResponse);
getLastModified(HttpServletRequest);

11.4. ServletRequest interface

getAttribute(String);
getContentLength();
getInputStream();
getLocalPort();
getParameter();
getParameterNames();
...

11.5. HttpServletRequest extends ServletRequest

Also an interface, but add methods related to HTTP such as cookies.

getContextPath();
getCookies();
getHeader(String);  # request.getHeader("User-Agent") The client's platform and browser info
getQueryString();
getSession();
getMethod();

11.6. ServletResponse interface

getBufferSize();
setContentType();
getOutputStream();
getWriter();
...

11.7. HttpServletResponse extends ServletResponse

addCookies();
addHeader();
enccodeRedirectURL();
sendError();
setStatus();
...

11.8. ServletContext interface

getInitParameter(String);
getInitParameterNames();
getAttribute(String);
getAttributeNames()
setAttribute(String, Object);
removeAttribute(String)
...
getRequestDispathcer(String);