Thursday, October 14, 2010

Deploying Java Servlet for Web Application in First Tomcat JVM Instance


Setting up Java Servlet Layout

To follow the Java Servlet Specification for the new "sales" web application, I created the class directory for the Java class files under the new directory $CATALINA_BASE/webapps/sales/WEB-INF, see also Packaging Web Components. The WEB-INF directory is protected from access by browsers, meaning they are unbrowsable and safe from client views. The classes directory under WEB-INF is where web components and server-side utility classes should go. To create the WEB-INF and classes directories, run the following command:
$ mkdir -p $CATALINA_BASE/webapps/sales/WEB-INF/classes
JAR Files

Most Java servlets also need JAR (Java ARchive) files which should be put under the lib directory. Since it's a good practice to keep the application separate from the Tomcat distribution directory tree, I created a new lib directory under $CATALINA_BASE/webapps/sales/WEB-INF which is consistent with WAR's hierarchical directory structure.
$ mkdir $CATALINA_BASE/webapps/sales/WEB-INF/lib
The Java servlet example below requires the servlet-api.jar JAR file. This JAR is already available in the Tomcat distribution directory tree $CATALINA_HOME/lib. You could copy this JAR file to the application's new lib directory $CATALINA_BASE/webapps/sales/WEB-INF/lib, but then you would get the following warning in the $CATALINA_BASE/logs/catalina.out log file when you startup Tomcat:
INFO: validateJarFile(/opt/tomcat-instance/sales.example.com/webapps/sales/WEB-INF/lib/servlet-api.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class

Tomcat shows this warning since it tries now to load the JAR file twice, first from $CATALINA_HOME/lib and then from $CATALINA_BASE/webapps/sales/WEB-INF/lib. Even though it's not going to be a problem for Tomcat, it's better not to keep JARs in two places. Since the servlet-api.jar JAR file already exists in the Tomcat distribution directory, I did not copy it to the $CATALINA_BASE/webapps/sales/WEB-INF/lib directory. I use this directory for application specific JARs that don't come with the Tomcat distribution. You could also remove the JAR in $CATALINA_HOME/lib but remember that it will reappier the next time you upgrade the Tomcat software.
Creating a Java Servlet

Since server-side classes are supposed to go to the WEB-INF/classes directory, I created the following class file example under $CATALINA_BASE/webapps/sales/WEB-INF/classes (/opt/tomcat-instance/sales.example.com/webapps/sales/WEB-INF/classes) and saved it as Sales.java:
$ cat $CATALINA_BASE/webapps/sales/WEB-INF/classes/Sales.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class Sales extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Sales Page</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Executing Sales ...</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}
To compile the new Java servlet, the servlet-api.jar JAR file is needed which can be specified with either the -classpath option or the CLASSPATH environment variable. The -classpath option for SDK tools is preferred over the CLASSPATH environment variable since it can be set individually for each application without affecting others. In the following example I specify the path of the class directory with the basename '*' (if you are unfamiliar with basename, see 'man basename'). This is equivalent to specifying all files with the extensions .jar or .JAR files in the directory and therefore individual JAR files like servlet-api.jar don't need to be specified.

The following command should now compile the Java servlet without errors:
$ cd $CATALINA_BASE/webapps/sales/WEB-INF/classes
$ javac -classpath "$CATALINA_HOME/lib/*" Sales.java
$ ls
Sales.class  Sales.java
$
Configuring the Java Servlet

To configure servlets and other components for an application, an XML file called web.xml needs to be configured. The format of this file is defined in the Java Servlet Specification. In Tomcat, this file exists in two place:
$CATALINA_BASE/conf/web.xml
  $CATALINA_BASE/webapps/{your-appname}/WEB-INF/web.xml
The first one is the default web.xml file which is the base for all web applications in a Tomcat JVM instance, and the latter one is for the web application where WEB-INF resides for overwriting application specific settings.

For the newly created Java servlet "Sales" I created a new web.xml file under $CATALINA_BASE/webapps/sales/WEB-INF:
$ cat $CATALINA_BASE/webapps/sales/WEB-INF/web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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>servlet_sales</servlet-name>
    <servlet-class>Sales</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>servlet_sales</servlet-name>
    <url-pattern>/execute</url-pattern>
  </servlet-mapping>

</web-app>
For each servlet there is a <servlet> element. It identifies the servlet name (<servlet-name>) and the Java class name (<servlet-class>). The servlet mapping (<servlet-mapping>) maps a URI to the servlet name (<servlet-name>). In the above example "/execute" in "http://localhost:8080/mysales/execute" maps to "servlet_sales" which points to the "Sales" servlet class. Note that the order of these elements is important. So when you open the URL "http://localhost:8080/mysales/execute", the "Sales" Java servlet will be executed.

In the following example I updated the $CATALINA_BASE/webapps/sales/index.html file to provide an entry point to the new Java servlet:
$ cat $CATALINA_BASE/webapps/sales/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD><META http-equiv=Content-Type content="text/html"></HEAD>
<BODY>
<H3>Apache Tomcat Sales Home Page</H3>
<a href="/mysales/execute">Execute Sales</a>
</BODY>
</HTML>
$

Testing and Executing the Java Servlet

Note that if you run javac with the -classpath option or the CLASSPATH environment variable in the same shell before you startup Tomcat, you will get java.lang.NoClassDefFoundError / java.lang.ClassNotFoundException errors in your browser when you execute a servlet. To avoid this, simply re-login as the tomcat user before you startup Tomcat:
# su - -s /bin/sh tomcat
$ source /opt/tomcat-instance/sales.env
$ $CATALINA_HOME/bin/shutdown.sh
$ $CATALINA_HOME/bin/startup.sh
After Tomcat restarted, open the URL http://localhost/mysales (or use the server name instead of localhost) and you should see the "Execute Sales" link. Clicking on this link should invoke the Java servlet and display "Executing Sales" in your browser. If you are presented with an empty page instead, review the above steps and make sure you didn't miss a step. Check also the log files under $CATALINA_BASE/logs.

No comments:

Post a Comment