Web Applications with Java/JSP

by Christopher Schultz

All the cool new programming languages, like Ruby, always have compilers/interpreters and tools for Linux, and the old UNIX standbys like Tcl/Tk are still around when you need them. Why, then, is Java not a ubiquitous player in the Linux arena?

Linux and Java really do have a lot to offer each other. Both are rock-solid and scalable server-class software systems, and most college and university graduates with software-related degrees are familiar with them, making for a powerful combination. In this article, I introduce you to Java Web applications through the Java Servlet Specification, the Java programming language itself and Java Server Pages. These three tools can help you get a Web application running in a lot less time than you think.

The Java Servlet Specification

The Java Servlet Specification defines a Servlet Container, a Web application and the Servlet API, which is the glue that holds these pieces together.

A Servlet Container is analogous to a Web server, but it also knows how to deploy and manage Web applications, and so it often is known as an Application Server. The Servlet Container provides services that support the Servlet API, which is used by the Web application to interact with HTTP requests and responses.

Java Web Applications

A Java Web application is a self-contained collection of configuration files, static and dynamic resources, compiled classes and support libraries that are all treated as a cohesive unit by the Servlet Container. They are somewhat different from standard LAMP-style Web applications, which are more like collections of associated programs or scripts than formally defined, self-contained units. To demonstrate a Java Web application, I have developed a simple “timesheet” featuring some of the standard Java libraries that helped me write it.

Typically, a Web application is packaged in a WAR (Web ARchive) file, which is just a ZIP file with a special directory structure and configuration file layout. The directory structure of the Web application logically and physically separates these types of files. The WEB-INF directory contains all the configuration files, a lib directory contains all libraries (packaged in JAR, or Java ARchive files), and a classes directory contains the application's compiled code. Listing 1 shows the file layout of the Web application for reference.

Listing 1. Contents of timesheet.war

index.jsp
tasks.jsp
WEB-INF/web.xml
WEB-INF/lib/jstl-impl-1.2.jar
WEB-INF/lib/jstl-api-1.2.jar
WEB-INF/classes/lj/timesheet/Task.class
WEB-INF/classes/lj/timesheet/GetTasksServlet.class
WEB-INF/classes/lj/timesheet/BaseServlet.class
WEB-INF/classes/lj/timesheet/Client.class
WEB-INF/classes/lj/timesheet/SaveTaskServlet.class
WEB-INF/classes/ApplicationResources_en.properties
WEB-INF/classes/ApplicationResources_de.properties
WEB-INF/classes/ApplicationResources.properties
WEB-INF/classes/ApplicationResources_es.properties
WEB-INF/classes/ApplicationResources_fr.properties
META-INF/context.xml
META-INF/MANIFEST.MF

The WEB-INF directory also contains a special file, web.xml, which is known as the Web application's deployment descriptor. It defines all the behaviors of the Web application, including URI mappings, authentication and authorization. Let's look at the deployment descriptor for this Web application.

Listing 2. web.xml


<?xml version="1.0" encoding="ISO-8859-1" ?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    version="2.5">

  <servlet>
    <servlet-name>get-tasks</servlet-name>
    <servlet-class>lj.timesheet.GetTasksServlet</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>save-task</servlet-name>
    <servlet-class>lj.timesheet.SaveTaskServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>get-tasks</servlet-name>
    <url-pattern>/tasks</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>save-task</servlet-name>
    <url-pattern>/save-task</url-pattern>
  </servlet-mapping>

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected Pages</web-resource-name>
      <url-pattern>/tasks</url-pattern>
      <url-pattern>/save-task</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>*</role-name>
    </auth-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Timesheets</realm-name>
  </login-config>

  <security-role>
    <description>Users of the timesheet application</description>
    <role-name>user</role-name>
  </security-role>
</web-app>

You can see that each servlet is defined in a <servlet> element that defines the Java class that contains the code, as well as a name for the servlet (to be used later). After the servlets have been defined, they are then mapped (by name) to incoming URIs using <servlet-mapping> elements. This servlet mapping may seem tedious and verbose, but it can be very powerful for several reasons:

  1. You can map one servlet to multiple URIs.

  2. You can use wild-card mappings (/foo/bar/*).

  3. You may not want to reveal any of the code's structure to remote visitors.

  4. You may have servlets you don't want to map at all.

After the servlet mappings come container-managed authentication and authorization. The Servlet Specification requires that Servlet Containers provide mechanisms for authentication and authorization, and the configuration in the Web application is declarative: web.xml simply specifies what resources are protected and who is allowed to access them, using role-based authorization constraints. The setup is quite straightforward, and the Web application becomes simpler by not having to implement that capability inside the application. In this application, I've chosen to use HTTP BASIC authentication to simplify the application. DIGEST, FORM and (SSL) CLIENT-CERT are other options allowed by the Servlet Specification.

Container Configuration

You may be wondering how the Servlet Container knows anything about the database. The answer is found in another configuration file, specific to each Servlet Container, that includes this information. You can look at the conf/context.xml file that comes with the sample Web application files for this article (see Resources), but you'll have to refer to the Apache Tomcat Web site for details on this Tomcat-specific configuration file format. If you want to deploy the sample application on a different application server, you need to write your own container-specific configuration file, which includes your database configuration.

Java Servlets

Now that you have a sense of how the Web application is packaged and deployed, let's turn our attention to the real action in the Web application: the code.

Java is both a programming language and a runtime environment, much like Perl and PHP. In those cases, the compiler generally is invoked when the script is executed, while Java is always compiled beforehand. The Java programming language itself is object-oriented, procedural, block-structured and entirely familiar to anyone who has written in a C-like language. It has a number of explicitly defined primitive data types as well as reference types. All the Java code you write lives within the definition of a class, including servlet code.

Handling a Request

Let's take a look at the source code for the GetTasksServlet (Listing 3), which implements the “get-tasks” servlet, which is mapped to the URL /tasks.

Listing 3. GetTasksServlet.java


package lj.timesheet;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

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

public class GetTasksServlet
    extends BaseServlet
{
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException
    {
        String username = request.getUserPrincipal().getName();

        try
        {
            List<Client> clients = getClients();

            // Convert client list to lookup table
            Map<Integer,Client> clientMap
                = new HashMap<Integer,Client>(clients.size());

            for(Client client : clients)
                clientMap.put(client.getId(), client);

            request.setAttribute("clients", clients);
            request.setAttribute("clientMap", clientMap);
            request.setAttribute("tasks", getTasks(username));

            getServletContext().getRequestDispatcher("/tasks.jsp")
                .forward(request, response);
        }
        catch (SQLException sqle)
        {
            throw new ServletException("Database error", sqle);
        }
    }

    ...
}

The first line of the file declares the “package” in which the class is defined. Packages help keep code organized and have implications on variable, method, and class scope and visibility. The next set of lines are “imports” that indicate to the compiler which classes will be referenced by this class. Those classes beginning with java. are standard Java classes, while those beginning with javax.servlet are those provided by the Java Servlet Specification. Then, we define a class called GetTasksServlet that extends an existing class called HttpServlet, the basis for all HTTP-oriented servlets. The HttpServlet class defines a number of doXXX methods, where XXX is one of the HTTP methods, such as GET (doGet), POST (doPost), PUT (doPut) and so on. I have overridden the doGet method in order to respond to HTTP GET requests from clients.

The doGet method accepts two parameters: the request and the response, which provide hooks into the resources provided by the Servlet Container and to the information provided by the client for a particular HTTP request. I use two utility methods (defined later in the class) to obtain a list of clients and a list of tasks, and store them in the request object's “attributes”, a location where data can be placed in order to pass them between stages of request processing. You'll see how to access this information next when I cover JSP files for generating content. Finally, I invoke the “request dispatcher's” forward method, which tells the container to forward the request to another resource: tasks.jsp.

Java Server Pages

Java Server Pages (JSPs) is a technology for dynamic content generation for things like Web pages. JSPs are analogous to PHP pages, where static text can be mixed with Java code, and the result is sent to the client. Technically speaking, JSPs are translated on the fly by a special servlet (provided by the Servlet Container) into their own servlets and compiled into bytecode, and then run just like “normal” servlets. Listing 4 shows the code for tasks.jsp—the page referenced in GetTasksServlet's doGet() method above.

Listing 4. tasks.jsp


<%@ page
  pageEncoding="UTF-8"
%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<fmt:setBundle basename="ApplicationResources" />
<html>
  <head>
    <title><fmt:message key="tasks.title" /></title>
  </head>

  <body>
    <h1><fmt:message key="tasks.title" /></h1>

    <table>
      <tr>
        <th><fmt:message key="tasks.header.date" /></th>
        <th><fmt:message key="tasks.header.client" /></th>
        <th><fmt:message key="tasks.header.description" /></th>
        <th><fmt:message key="tasks.header.duration" /></th>
      </tr>

    <c:forEach var="item" items="${tasks}">
      <tr>
        <td>
          <fmt:formatDate dateStyle="short" value="${item.date}" />
        </td>
        <td><c:out value="${clientMap[item.clientId].name}" /></td>
        <td><c:out value="${item.description}" /></td>
        <td><c:out value="${item.duration}" /></td>
      </tr>
    </c:forEach>
    </table>

    <form method="POST" action="<c:url value="/save-task" />">
      <fieldset>
        <legend>Add New Task</legend>

        <div class="form-field">
        <label for="clientId">
          <fmt:message key="tasks.header.client" />
        </label>
        <select name="clientId" id="clientId">
          <option value="">Please choose&hellip;</option>
          <c:forEach var="client" items="${clients}">
            <option value="${client.id}">
              <c:out value="${client.name}" />
            </option>
          </c:forEach>
        </select>
        </div>

        <div class="form-field">
        <label for="description">
          <fmt:message key="tasks.header.description" />
        </label>
        <input type="text" name="description"
               id="description" size="50" />
        </div>

        <div class="form-field">
        <label for="duration">
          <fmt:message key="tasks.header.duration" />
        </label>
        <input type="text" name="duration" id="duration" size="4" />
        </div>

        <div class="buttons">
          <input type="submit"
                 value="<fmt:message key="task.save" />" />
        </div>
      </fieldset>
    </form>
  </body>
</html>

The page begins with a page declaration that includes some metadata about the page, including the output character encoding, and then some “taglib” tags that tell the JSP compiler I want to use some “tag libraries”. Tag libraries are helper libraries that allow JSP scripts to wield powerful tools using very simple syntax. After the DOCTYPE, there is a <fmt:setBundle> element, and in the <title> of the page, there is a <fmt:message> element. These two tags, defined by the “fmt” tag library, work together to provide internationalization capabilities to this page. The <fmt:setBundle> tag defines the string resource bundle to be used by the page, and the <fmt:message> tag uses that bundle to pull localized text from the appropriate file to display in the page. The result is, when I visit this page with my Web browser set to the en_US locale, I get text in English, but if I switch the locale to fr_BE and reload the page, the page will switch into French without any further programming.

The standard Java API actually provides all this capability out of the box, but the JSTL (Java Standard Template Library) “fmt” tag library gives us access to Java's internationalization APIs without having to write any Java code. By providing a Java property file (a text file with simple key=value syntax) for each locale I want to support, I get text localization practically for free. Further down in the JSP file, you can see the use of another “fmt” tag, <fmt:formatDate>. This tag formats a date object using the user's locale and a simple name for the format (“simple” in this case). This results in MM/dd/yy in the US and dd/MM/yy in Belgium.

The next JSTL tag is <c:forEach>. This tag actually encloses a body, which is evaluated multiple times: once for each item it finds in the “items” attribute. The value of ${items} means that the value is not just a simple literal value, but an expression that should be evaluated. The object “items” is found in the request object's “attributes”—remember I put it there in the servlet code—and used here as the data for the loop. Within the body of <c:forEach>, the “item” object is defined and can be used by any JSTL tags.

A Note about Scoping

The Java Servlet Specification defines three data scopes: application, session and request. The JSP Specification adds a fourth one: page. Each of these scopes is a place where data can be stored by the Web application for use at any time. When object identifiers are used in expressions, they are looked up in each scoping level until an object is found. First, the page scope is searched, then the request, then the session and then the application. This is how tags like <c:forEach> can define new object names, and the tags within the body can access them.

The next tag, <c:out>, outputs a value in a Web-safe manner. If the value contains any < characters, they will be escaped to avoid nasty XSS attacks. The value of ${clientMap[item.clientId].name} is again an expression that tells <c:out> to take the client ID from the item object, use that to look up a value in the “clientMap”, and then get its name. The objects “item” and “clientMap” are both retrieved from the request attributes, and the <c:out> tag handles the expression evaluation and output escaping for us.

This page includes a form that allows us to enter new tasks. One of the most important attributes of the <form> is the “action”, which, of course, tells the form where the data should be sent. I use the <c:url> tag here to generate a URL for us. It may seem silly to use a tag when I simply could have used /timesheet/save-task as the value of the action attribute, but there are some subtle issues in play here, which must be taken into account. First, a Web application can be deployed into any “context path”, which means that the path to the servlet might actually be /my-timesheet/save-task. The <c:url> tag knows where the Web application has been deployed (courtesy of the request object, defined by the Servlet API) and can provide the appropriate path prefix to the URL. Second, <c:url> can encode the URL with a session identifier, which is essential to providing a good user experience for many Web applications. The <c:url> tag is smart enough to omit the session identifier from the URL if the client is using cookies to communicate the session identity to the server, but to include it in the URL as a fallback when cookies are unavailable. Sessions are another handy feature defined by the Servlet Specification, provided by the Servlet Container and accessible via the Servlet API.

Accepting Form Submissions

Now that I've covered the display of the timesheet and the form that can be used to submit a new task, let's take a look at the code that accepts this form submission: SaveTaskServlet.java (Listing 5), which implements the “save-task” servlet, which is mapped to the URL /save-task.

Listing 5. SaveTasksServlet.java

package lj.timesheet;

import java.io.IOException;

import java.util.Date;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;

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

public class SaveTaskServlet
    extends BaseServlet
{
    public void doPost(HttpServletRequest request,
                       HttpServletResponse response)
        throws ServletException, IOException
    {
        Integer taskId;

        if(null == request.getParameter("id")
           || "".equals(request.getParameter("id").trim()))
            taskId = null;
        else
            taskId = new Integer(Integer.parseInt(
                             request.getParameter("id")));

        int clientId = Integer.parseInt(
                           request.getParameter("clientId"));
        Date date = new Date();
        String description = request.getParameter("description");
        int duration = Integer.parseInt(
                           request.getParameter("duration"));

        String username = request.getUserPrincipal().getName();

        Task task = new Task(taskId, username, date,
                             clientId, description, duration);

        try
        {
            save(task);

            response.sendRedirect(response.encodeRedirectURL(
                request.getContextPath() + "/tasks"));
        }
        catch (SQLException sqle)
        {
            throw new ServletException("Database error", sqle);
        }
    }

    // see below
}

The SaveTaskServlet overrides the HttpServlet's doPost method so we can handle HTTP POST messages. It gathers the data from the request, made available through the request object's getParameter method, then creates a Task object and calls a helper method (defined later in the class) called “save”. After saving the new task, the user is redirected to the “tasks” servlet to view the updated list of tasks. Did you notice that the line of code performing the redirect calls response.encodeRedirectURL and prepends the context path to the target URI? This is precisely the tedium that is avoided in JSP files by using the <c:url> tag.

SaveTaskServlet also defines a “save” method that interacts with the database. While none of this code is servlet-oriented, it's instructive to see the power of some of Java's standard APIs. In this case, it's the JDBC API that gives us access to relational databases (Listing 6).

Listing 6. SaveTaskServlet.java

    public Task save(Task task)
        throws SQLException
    {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try
        {
            conn = getConnection();

            if(null == task.getId())
            {
                // A new task
                ps = conn.prepareStatement(
                         "INSERT INTO
                              task (owner, date, client_id,
                                    description, duration)
                              VALUES (?,?,?,?,?)",
                         PreparedStatement.RETURN_GENERATED_KEYS);

                ps.setString(1,  task.getOwner());
                ps.setTimestamp(2, new Timestamp(
                                           task.getDate().getTime()));
                ps.setInt(3, task.getClientId());
                ps.setString(4, task.getDescription());
                ps.setInt(5, task.getDuration());

                ps.executeUpdate();

                rs = ps.getGeneratedKeys();

                if(!rs.next())
                    throw new SQLException(
                        "Expected auto-generated key, got none");

                int taskId = rs.getInt(1);

                if(rs.wasNull())
                    throw new SQLException(
                        "Got bogus auto-generated key");

                task = new Task(taskId,
                                task.getOwner(),
                                task.getDate(),
                                task.getClientId(),
                                task.getDescription(),
                                task.getDuration());
            }
            else
            {
                ps = conn.prepareStatement(
                         "UPDATE task SET date=?, client_id=?,
                                          description=?, duration=?
                             WHERE id=? AND owner=?");

                ps.setTimestamp(1, new Timestamp(
                                           task.getDate().getTime()));
                ps.setInt(2, task.getClientId());
                ps.setString(3, task.getDescription());
                ps.setInt(4, task.getDuration());
                ps.setInt(5, task.getId());
                ps.setString(6, task.getOwner());

                ps.executeUpdate();
            }

            return task;
        }
        finally
        {
            close(conn, ps, rs);
        }
    }

First, this method obtains a connection from a database connection pool and then determines if the Task is being created from scratch or updated (although our UI doesn't offer an “update” method yet, this class has been designed to allow updates). In each case, a parameterized SQL statement is prepared and then filled with data passed in from the calling code. Then, the statement is executed to write to the database, and a new object is passed back to the caller. In the case of a new task, the database-generated primary key is fetched from the statement after execution in order to pass it back to the caller.

Under normal circumstances, methods such as “save” would be split out into a separate class for easier organization, testing and architectural separation, but I've left them in the servlet classes for simplicity.

The example's full source code and prebuilt WAR file are available from the Linux Journal FTP server (see Resources), and I encourage you to download it and play around with it. I've also included quick installation instructions for Java and the Apache Tomcat servlet container, which will be required to run the example application.

Java and Model-View-Controller Architecture

Often, Perl and PHP-based Web applications are composed of self-contained scripts that perform one task: loading and displaying tasks, for instance. This kind of thing is entirely possible using nothing but JSPs. There are tag libraries that perform SQL queries, and you even can write Java code directly into a JSP, although I haven't covered it here because it's not necessary with the rich tools provided by the JSTL. On the other hand, there are some philosophical and practical reasons not to stuff everything into a single JSP. Most (Java) programmers subscribe to the “model-view-controller” architecture, where code is separated into logical units that model your problem domain (that would be the Task and Client objects in our example), provide views of your data (that's our JSPs) and control program flow (the servlets). This architectural separation actually leads to quite a few practical benefits, including:

  1. Easier code maintenance: separation promotes code re-use and simplifies automated testing.

  2. Error handling: if the controller is the only likely component to fail (due to bad input, db connection failure and so on), you don't have to worry about the view component failing during rendering, ruining your output.

Most Java projects are going to be split up in this way, so I wrote my example to illustrate this architecture, and I hope you consider using this architecture in your Java projects too.

Conclusion

Adding Java to your repertoire for building Web applications gives you access to the built-in services guaranteed by the Servlet Specification as well as a plethora of high-quality third-party libraries. Servlet containers provide many services useful to your Web applications through simple configuration and/or APIs. Java Server Pages can be used to build complex Web pages quickly while avoiding business logic. The Servlets you write to implement your business logic have full access to many APIs for just about anything you can think of. The power of Java Web applications and the stability and scalability of Linux can be combined into a platform on which many high-quality on-line services are built, including mine. I hope I've given you a taste of how easy it is to create a robust and useful Java Web application using the tools provided by the Java Servlet Specification, and that you consider using Java for your next Web application.

Resources

Example Web Application for This Article: ftp.linuxjournal.com/pub/lj/listings/issue197/10810.tgz

Java Servlet Specification (version 2.5): jcp.org/aboutJava/communityprocess/mrel/jsr154/index2.html

JavaServer Pages Standard Tag Library: https://jstl.dev.java.net

Apache Tomcat Web Site: tomcat.apache.org

Christopher Schultz is the CTO of Total Child Health, Inc., a healthcare software company based in Baltimore, Maryland. He has been developing Web applications in Java since those words could reasonably be placed in the same sentence. He is an active member of the Apache Tomcat users' mailing list, and he is a committer on the Apache Velocity Project. He lives in Arlington, Virginia, with his wife Katrina, son Maxwell and dog Paddy.

Load Disqus comments