InformIT

Server-Side Web Programming with Jython

Date: May 17, 2002

Sample Chapter is provided courtesy of New Riders.

Return to the article

Learn about Servlets and JSP written with Jython—how to set up a Servlet container, the basic Servlet classes, implementing cookies, sessions and database connections, and using Jython with JSP pages.

Where does Jython fit into web development? The answer is wherever Java fits. Jython especially fits those Java venues where people desire faster development and increased flexibility. Server-side Java web programming is primarily implemented as Servlets and Java Server Pages (JSP); therefore, Jython's primary implementation is Servlets and JSP. This isn't the entire story. Java's enterprise packages (j2ee) figures prominently in developing web applications. EJB's, JNDI, JDBC and more are integral to most Java web applications, and Jython is equally effective with all those technologies; however, the primary focus of this chapter is Servlets and JSP.

There are also places where Jython does not fit. CPython is a very popular language for implementing CGI scripts, but Jython is not. In CGI, a web server receives a request, starts a process to respond, and shuts down that sub-process when the response is completed. It is possible to use Jython in this manner, but it is not a good way. The startup time of the JVM itself makes this a poor choice. Servlets and JSP on the other hand, are persistent—they remain in memory between web requests.

There are many advantages to using Jython for web programming. High-quality Servlet containers are readily available and near ubiquitously deployed. Java Servlet applications can benefit from Jython's flexibility and high-level language characteristics, and you get to leverage all that Java's and Jython's libraries have to offer. Some of the many quality Servlet containers include WebLogic, WebSphere, Tomcat, Jigsaw, Resin, and Jetty. The number of organizations that have deployed such containers is astounding, which makes Jython immediately usable in a huge percentage of situations.

What you can expect in this chapter is Servlets and JSP written with Jython. The basic topics include setting up a Servlet container, the basic Servlet classes, implementing cookies, sessions and database connections, and using Jython with JSP pages. The slightly more advanced topic of creating your own Jython Servlet mappings appears after the basics. Implementation-specific topics appear toward the end of this chapter, and they include topics such as templates, XML, Cocoon, IBM's Bean Scripting Framework, and Enterprise Java Beans.

Jython Servlet Containers

Jython works with any compliant Java Servlet container, and there are a great many of these from which to choose. This chapter uses Tomcat, which is the reference implementation of the Servlet and Java Server Page specifications. Some of the popular and freely available Servlet containers are Tomcat from Apache's Jakarta project, Apache's JServ, the Jigsaw web server from the W3C and Jetty from Mort Bay Consulting. Following is a brief description of each of these tools.

Jakarta's Tomcat is the reference implementation of the Servlet and Java Server Pages specifications, and the server used in this chapter. Tomcat is available at http://jakarta.apache.org/ and the stable release version as of the writing of this chapter is 3.2.3. This version of Tomcat supports the 2.2 Servlet and 1.1 JSP specifications. By the time you read this, Tomcat 4.0 will have been released, which implements the 2.3 Servlet and 1.2 JSP specifications. All Jython Servlets in this chapter were tested with Tomcat 3.2.3. All examples should work with any Servlet 2.2/JSP 1.1-compliant container available according to Sun's "Write Once, Run Anywhere" motto. The Tomcat distribution includes the Servlet and JSP classes that are required for this chapter, so there are no additional downloads required.

Apache JServ is a Servlet (version 2.0) engine created for Apache and is commonly deployed. This is an easy means of using Servlets with Jython and may be a good choice if your current development already employs JServ as many do. Information about Apache and JServ can be found at http://java.apache.org/. JServ requires the accompanying Java Servlet Development Kit 2.0 available separately at http://java.sun.com/products/

servlet/index.html. Java Server Pages require an external module that is currently located at http://java.apache.org/jserv/.

Jigsaw is the W3C's experimental web server. The term "experimental" may be misleading because it is more mature than that label indicates. Jigsaw is a fully HTTP/1.1-compliant web server and caching proxy server written entirely in Java. Jigsaw also supports the Servlet 2.2 specification and Java Server Pages 1.1. Jigsaw is available at http://www.w3.org/Jigsaw/ and includes the required Servlet and JSP files.

Jetty is a compact and efficient Java web server that supports the Servlet 2.2 and JSP 1.1 specifications, supports HTTP 1.1, includes SSL support, and easily integrates with EJB servers such as JBoss. Jetty is available at http://jetty.mortbay.com/.

The documentation for these tools is extensive, so installation guidelines for these should be gleaned from their respective web sites.

Defining Simple Servlet Classes

This section compares a simple Java Servlet with a simple Jython Servlet. The section on testing these Servlets describes how to install the jython.jar file in a Tomcat web application.

A Simple Java Servlet

Listing 12.1 is the most basic of Java Servlets.

Listing 12.1 A Basic Java Servlet

""'// Filename: JavaServlet.java
import javax.servlet.*;
import java.io.*;

public class JavaServlet extends GenericServlet {

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

    res.setContentType("text/html");
    PrintWriter toClient = res.getWriter();

    toClient.println("<html><body>" +
             "This is a Java Servlet." +
             "</body></html>");
  }
}

A base class from the Servlet API is required, and Listing 12.1 uses GenericServlet, imported from the javax.servlet package in the first line. The service method in GenericServlet is abstract, so it must be implemented in any subclasses. This is the method invoked in response to a web request for JavaServlet (the name of the file in Listing 12.1). Output destined for the client is sent through a PrintWriter object retrieved from the ServletResponse object. This could also be an OutputStream for binary data.

A Simple Jython Servlet

Implementing a Jython Servlet comparable to Listing 12.1 should be similar in what it imports, inherits, and implements, but done in Jython's syntax. Listing 12.2 demonstrates.

Listing 12.2 A Basic Jython Servlet

# Filename: JythonServlet.py
from javax import servlet
import random # Not used, just here to test module imports

class JythonServlet(servlet.GenericServlet):
  def service(self, req, res):
    res.setContentType("text/html")
    toClient = res.getWriter()
    toClient.println("""<html><body>
             This is a Servlet of the Jython variety.
             </body></html>""")

Listing 12.2 is a subclass of GenericServlet just like its Java counterpart in Listing 12.1. It also implements the service() method of GenericServlet as required. Syntactic differences in Listing 12.2 include parentheses after the class definition to designate the superclass, the omission of the throws statement, an explicit self parameter in the service method, and, of course, the absence of semicolons and explicit type declarations. Additionally, the import statements differ in Jython. As stated earlier, the from module import * syntax is strongly discouraged; instead, Listing 12.2 imports the parent package. One additional import in Listing 12.2 is the random module. This module is not actually used, and only exists to test module imports.

Testing the Java and Jython Servlets

Testing the Servlets in Listings 12.1 and 12.2 requires the installation of Tomcat. The Jython Servlet in Listing 12.2 additionally requires including the jython.jar file in Tomcat. This section first describes the steps to installing Tomcat, then addresses the installation and testing of the two Servlets discussed.

Installing Tomcat

The first step is to download Tomcat from http://jakarta.apache.org. This section addresses the installation of a binary release of Tomcat in stand-alone mode. The suggested version to download is jakarta-tomcat-3.2.3. Download the zip or tar.gz file appropriate for your platform.

Next, unzip or untar the archive to the directory in which you have sufficient permissions. If you unzip the archive into the C:\jakarta-tomcat-3.2.3 directory, this becomes the Tomcat home directory. If you use /usr/local/jakarta-tomcat-3.2.3, this becomes the Tomcat home directory. You should set an environment variable to the Tomcat home directory. For the directory C:\jakarta-tomcat-3.2.3 on Windows, add the following to your environment settings:

set TOMCAT_HOME=c:\jakarta-tomcat-3.2.3

For the directory /usr/local/jakarta-tomcat-3.2.3 on *nix, add the following to your bash environment:

export TOMCAT_HOME=/usr/local/jakarta-tomcat-3.2.3

If you do not set the TOMCAT_HOME environment variable, you must start Tomcat from within its home or bin directory.

Next, set the environment variable JAVA_HOME to the root directory of your JDK installation. Here's an example using JDK1.3.1:

# on Windows
set JAVA_HOME=c:\jdk1.3.1
# bash (*nix) setup
export JAVA_HOME=/usr/java/jdk1.3.1

The installation is complete. You can start Tomcat with the startup script appropriate for your platform:

# Windows
%TOMCAT_HOME%\bin\startup.bat
# bash (*unix)
$TOMCAT_HOME/bin/startup.sh

You should see startup information printed to the screen as the Tomcat server loads. An important line to look for is this:

date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080

This designates the port that you will be using to connect to the Servlet container: 8080 is the default. When you see this, Tomcat is running and ready to accept connections on port 8080.

When you wish to stop Tomcat, use the following shutdown scripts:

# Windows
%TOMCAT_HOME%\bin\shutdown.bat
# bash (*nix)
$TOMCAT_HOME/bin/shutdown.sh

The Servlet 2.2 specification designates a directory hierarchy for web applications that begins in the directory %TOMCAT_HOME%\webapps. Folders within this directory are web applications, or contexts, and each follows a specific directory hierarchy. The examples in this chapter use the context named jython. The directory structure required for the jython context is shown here:

%TOMCAT_HOME%\webapps\
%TOMCAT_HOME%\webapps\jython          The context's root
%TOMCAT_HOME%\webapps\jython\WEB-INF
%TOMCAT_HOME%\webapps\jython\WEB-INF\classes  Servlet classes
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib    Library archives

You should create these directories before continuing with the examples. If you restart Tomcat, you should see an additional line of information when it restarts. The following line confirms that Tomcat has loaded the new context:

date time - ContextManager: Adding context Ctx( /jython )

Installing the Java Servlet

To install the Java Servlet from Listing 12.1, first place the JavaServlet.java file in the directory %TOMCAT_HOME%\webapps\jython\WEB-INF\classes. This directory is the root directory for class files. Because JavaServlet is not within a package, it belongs in the root of the classes directory. Note that Listing 12.1 is not within a package; if you had chosen to designate a package, such as the package demo for example, you would have then placed the compiled class file in the classes\demo directory in order to comply with Java class directory structures. This is only a note for those Servlets placed within a package, which is not the case for our example, however.

From within that directory, compile the JavaServlet.java file with the following command:

javac -classpath %TOMCAT_HOME%\lib\servlet.jar JavaServlet.java

After compiling JavaServlet, you are ready to view it. First, start the Tomcat server, and then point your browser to http://localhost:8080/jython/servlet/JavaServlet. You should see the simple string message This is a Java Servlet.

Installing the Jython Servlet

There are two ways to use Jython Servlets. One is to compile the Servlet with jythonc and place the resulting class files in the %TOMCAT_HOM%\jython\WEB-INF\classes directory. The other is to use Jython's PyServlet mapping class. This section uses jythonc. The PyServlet mapping is often a better way to deploy Jython Servlets, but jythonc-compiled Servlets are equally sensible at times.

The three steps required to install a jythonc-compiled Servlet in Tomcat are as follows:

  1. Compile the Jython Servlet module with jythonc.

  2. Add the jython.jar file to the web application.

  3. Make the modules from Jython's lib directory available to Jython Servlets.

Compiling a Jython Servlet with jythonc

Compiling with jythonc requires that the servlet.jar file exists within the classpath. The servlet.jar file contains the javax.Servlet.* classes and packages, and it is found in Tomcat's lib directory (%TOMCAT_HOME%\lib). If you use jythonc to compile a Servlet without servlet.jar in the classpath, there are no errors or warnings during the compilation; however, when you try to run a Servlet compiled that way, you will get a java.lang.ClassCastException (at least that is the case for jythonc at the time of this writing).

Place the JythonServlet.py file from Listing 12.2 to the directory %TOMCAT_HOME%\jython\WEB-INF\classes. Ensure that your environment CLASSPATH variable does in fact include Servlet.jar, and then use jythonc to compile the Jython code into Java classes by using the following command from within the %TOMCAT_HOME%\jython\WEB-INF\classes directory:

jythonc –w . JythonServlet.py

Specifying the current working directory with the –w switch eliminates the need to copy the generated class files from the jpywork directory. There should be two class files in the classes directory. Remember that a compiled Jython file will have at least two associated class files. The files produced from compiling JythonServlet.py with jythonc should be JythonServlet.java, JythonServlet.class, and JythonServlet$_PyInner.class. Both the class files are required to use the Servlet and must both be in the WEB-INF\classes directory.

During the compilation with jythonc, it is important to look for the lines that read as follows:

  Creating .java files:
   JythonServlet module
    JythonServlet extends javax.servlet.GenericServlet

If you do not see these lines, something is wrong and the Servlet will not work. Double-check the CLASSPATH and setup options and compile the file again until you see the lines noted previously.

Adding jython.jar to the classpath

All Jython Servlets must have access to the classes within the jython.jar file. There are three ways to add the jython.jar file to Tomcat's classpath:

The preferred approach is to place the jython.jar file in the context's lib directory. The context should include the directory {context}\WEB-INF\lib. For the jython context used in this chapter, it is %TOMCAT_HOME%\webapps\jython\WEB-INF\lib. Class archive files, such as jython.jar, belong in this directory. This is preferred because it makes a self-contained web application. As soon as a web application requires access to archives outside of its context, archiving, packaging, and installing the application on other servers becomes troublesome. You are strongly urged to keep all web applications self-contained unless you are sure it is not necessary in your situation.

You can also place the jython.jar file in Tomcat's lib directory (%TOMCAT_HOME%\lib). Jar files in this directory are automatically added to the classpath. However, this is the least preferred of the three approaches. You may reduce duplicate jython.jar files, but your web application is no longer self-contained. Additionally, you do not get automatic access to Jython's lib directory as you do with the third approach.

The third option is to leave the jython.jar file in Jython's installation directory, and add it to the classpath before starting Tomcat. This also eliminates duplicate jython.jar files; however, it has the added advantage of providing access to the registry file and Jython's lib directory. Remember that the registry is sought in the python.home directory, or in the location in which the jython.jar file was found if there is no python.home property. Leaving the jython.jar file in Jython's installation directory is therefore an advantage over placing it in Tomcat's lib directory. It's worth mentioning again, however, that a self-contained context is preferred.

Making Jython's lib Directory Available to Servlets

There are three ways to make the modules in Jython's lib directory available to Servlets:

If you chose to leave the jython.jar file in Jython's installation directory, no additional steps are required to gain access to Jython's lib directory. If you chose to place the jython.jar file in the context's lib directory, you must set the python.home property, explicitly append a directory to sys.path, or freeze the modules before your Jython Servlets can use the Jython module library.

To set the python.home property you can set the TOMCAT_OPTS environment variable. Before you do this, you must decide where the modules will be located. Again, the best way is to create a self-contained web application. A good recommendation is to create an additional directory within the context's WEB-INF directory. The name of this directory will be jylib for the purposes of this section. Create the directory %TOMCAT_HOME%\webapps\jython\WEB-INF\jylib. Because Jython looks for the lib directory within python.home, continue by also making the directory %TOMCAT_HOME%\webapps\jython\ WEB-INF\jylib\Lib. Place any required modules within this directory, and specify the jylib directory as the python.home. Here are some examples that set TOMCAT_OPTS to the proper python.home property setting:

# Windows
set TOMCAT_OPTS=-Dpython.home=%TOMCAT_HOME%\webapps\jython\WEB-INF\jylib

# bash (*nix)
export TOMCAT_OPTS=-Dpython.home=$TOMCAT_HOME/webapps/jython/WEB-INF/jylib

Note that some versions of Windows do not allow an equals sign in an environment string. In this case, you must edit the tomcat.bat file to include the python.home parameter setting.

It is possible to just begin Servlets by explicitly adding the module directory to sys.path. The problem is that this often requires explicit, machine-dependent paths, and thus limits cross-platform portability. Here is an example of what would appear at the top of a Servlet where you wish to explicitly append to the sys.path:

import sys
libpath = "c:/jakarta-tomcat_3.2.3/webapps/jython/WEB-INF/jylibs/Lib"
sys.path.append(libpath)

Another useful approach to making Jython's modules available is to freeze them. The jythonc ––deep option compiles all required modules, making the python.home and Jython's lib directory unimportant. To freeze the JythonServlet.py file from Listing 12.2, use the following command from within the Jython context's classes directory:

jythonc –w . ––deep JythonServlet.py

Class files for the JythonServlet and for all the modules it requires are now located within the context's classes directory. In other words, the Servlet and modules are now installed in a self-contained web application. Note that compiling other Jython Servlets this way will overwrite any previously compiled modules. This means you must be careful when updating modules, as a newer version may adversely affect an older Servlet. Compiling with the ––deep options creates a number of files, but the generated *.java files may be deleted after compilation.

Freezing is beneficial because changes to modules are infrequent, and because it is an easy way to make a fully self-contained web application. You don't need to set the python.home property. A web application set up this way can simply be archived as a .war file and plugged into any other compliant server without a single extra installation step required.

Testing the Jython Servlet

With the Servlet from Listing 12.2 compiled with jythonc, the jython.jar in the classpath, and Jython's modules accessible, you can now view it. Point you browser to http://localhost:8080/jython/servlet/jythonServlet. You should see the simple message "This is a Servlet of the Jython variety." If this is not what you see, the likely alternatives are one of three error messages. If the Servlet.jar file was not in the classpath while compiling the JythonServlet, you will likely see a ClassCastException. You will also see a ClassCastException if the filename and classname differ, even if only in capitalization. If one of the class files generated from compiling the Servlet with jythonc is in the context's classes directory, you will see an AttributeError. If Jython's modules are not available, you will see an ImportError.

More About GenericServlet

Listing 12.2 inherits from javax.Servlet.GenericServlet. This class is just what its name implies: a Servlet class that is not specific to any protocol. The HttpServlet class is more common for web programming because it is specific to the HTTP protocol; however, GenericServlet is HttpServlet's superclass, and its methods are important to know. Table 12.1 has a summary of Java method signatures for GenericServlet and examples of how a Jython subclass of GenericServlet would use those methods. These are not all the methods—just those easily used from Jython. Table 12.1 lists methods in two categories:

Table 12.1 GenericServlet Methods

Java Signature

Usage in Jython Subclass

Methods You Subclass:

public void init(ServletConfig config) throws ServletException;

def init(self, config):

public void destroy();

def destroy(self):

public abstract void service(ServletRequest request, ServletResponse response) throws Servletexception, IOException;

def service(self, request, response):

public String getServletInfo();

def getServletInfo(self): return "Info string"

Methods You Use with the self.Method Syntax:

public void log(String message);

self.log("message")

public ServletConfig getServletConfig();

config = self.getServletConfig()

public java.util.enumeration getInitParameterNames();

nameList = self.getInitParameterNames()

public String getInitParameter(String name)

param = self.getInitParameter("paramName")

public ServletContext getServletContext()

context = self.getServletContext


You have already seen an example of overloading the public abstract void service() method in Listing 12.2. The other methods available for overloading get attention in the following hit counter Servlet.

A hit counter Servlet is almost as obligatory as a Hello World application in the pedagogy of programming. Here it is in the name of tradition. The hitCounter Servlet (see Listing 12.3) shows the use of the init, service, and destroy methods. A discussion of each of these methods appears following the listing.

Listing 12.3 HitCounter Servlet

# filename: HitCounter.py
from javax import servlet
from time import time, ctime
import os

class HitCounter(servlet.GenericServlet):
  def init(self, cfg=None):
    if cfg:
      servlet.GenericServlet.init(self, cfg)
    else:
      servlet.GenericServlet.init(self)

    # Construct a path + filename to file storing hit data
    contextRoot = self.servletContext.getRealPath(".")
    self.file = os.path.join(contextRoot, "counterdata.txt")

    if os.path.exists(self.file):
      lastCount = open(self.file, "r").read()
      try: 
        # within 'try' just in case the file is empty
        self.totalHits = int(lastCount)
      except:
        self.totalHits = 0
    else:
      self.totalHits = 0

  def service(self, req, res):
    res.setContentType("text/html")
    toClient = res.getWriter()
    toClient.println("<html><body>")
    toClient.println("Total Hits: %i<br>" % (self.totalHits,))
    self.totalHits += 1
    toClient.println("</body></html>")

  def destroy(self):
    f = open(self.file, "w")
    f.write(str(self.totalHits))
    f.close()

Place the HitCounter.py in the context's classes directory (%TOMCAT_HOME%\webapps\jython\WEB-INF\classes), and compile it with jythonc from within the same directory.

jythonc –w . ––deep HitCounter.py

Test the HitCounter Servlet by pointing you browser to http://localhost:8080/jython/servlet/HitCounter. You should see the message "Total Hits: 0" The first time you visit this URL. Each subsequent hit increments the count. If you shut down Tomcat with %TOMCAT_HOME%\bin\shutdown.bat ($TOMCAT_HOME/bin/shutdown.sh for bash) and restart Tomcat, the hit count should continue at its last number.

The three methods init, service, and destroy are essential to Servlets, and each one matches a stage of a Servlet's life cycle. A Servlet is first loaded and initialized: the init() method. A Servlet then handles client requests: the service() method. A Servlet handles requests until it is unloaded or removed from the Servlet container(): the destroy method.

init(ServletConfig) Method

The init() method is called at Servlet startup, and only called that one time. This makes it useful for time-intensive tasks such as setting up database connections, compiling regular expressions or loading ancillary files. The purpose of including an init() method in Listing 12.3 is to establish the file that stores the hit information, and to set the counter to the last stored integer in that file. This implementation ensures that the hit counter is not reset to 0 unless the Servlet is restarted and the counter.txt file is missing.

The 2.2 version of the Servlet API has two init() methods: a parameterless version and one that accepts an instance of the ServletConfig class. Listing 17.3 uses the variable cfg for this object. It's important to remember that when Jython overrides a Java method, it overrides all methods with that name. This means that defining an init() method in a Jython Servlet overrides both the parameterless init() and the init(ServletConfig cfg) methods. Listing 17.3 handles the functionality of both methods by specifying a default argument of None for the cfg variable, then testing for None to determine if that cfg variable should be used when calling the superclass's init() method.

service(ServletRequest, ServletResponse) Method

This is the method called in response to each client request. A Servlet must define this method because it is abstract in GenericServlet. The arguments passed to this method are the ServletRequest and the ServletResponse objects. ServletRequest contains client information sent in the request to the server such as the request headers, request method, and request parameters. ServletResponse is used for sending the response stream in the appropriate mime-encoded format back to the client.

destroy() Method

This is called when shutting down or unloading a Servlet. Cleanup code goes here, including closing database connections, flushing and closing files, and so on.

The destroy method in Listing 12.3 writes the hit counter value to a file so that the information is not lost when the Servlet is unloaded. This isn't the best means of persistence, however. The hit count value only gets stored if the server is shutdown politely. If the Servlet container is not shutdown properly, the destroy method may not be called. When you are using Tomcat, you usually start and stop the server with the startup and shutdown shell scripts. These are the polite and proper ways to start and stop Tomcat; however, if you choose to stop Tomcat with an impolite "Ctrl+C", the proper shutdown sequence is skipped. Additionally, an error that unexpectedly terminated Tomcat causes the same results, and the hit count would be lost. Higher quality persistence would require that the data be stored in a way that the assignment and commit are in a transaction framework.

HttpServlet

GenericServlet is truly generic because it is applicable to any chat-type protocol; however, web development is mostly about the HTTP protocol. To chat in HTTP, javax.Servlet.http.HttpServlet is best to extend. HttpServlet is a subclass of GenericServlet. Therefore, the init, service, and destroy methods from GenericServlet are available when extending HttpServlet.

HttpServlet defines a method for each of the HTTP methods. These methods are doGet, doPost, doPut, doOptions, doDelete, and doTrace. When Tomcat receives a client request of the GET type, the requested Servlet's doGet() method is invoked to reply to that client. Additionally, HttpServlet has an HTTP-specific version of the service method. The only change in the HttpServlet service method over the GenericServlet's version is that HTTP-specific request and response objects are the parameters (javax.servlet.http.HttpServletRequest and javax.servlet.http.HttpServletResponse).

To write a Servlet by subclassing HttpServlet, implement each HTTP method desired (such as doGet or doPost), or define the service method. The HttpServlet class also has a getlastModified method that you may override if you wish to return a value (milliseconds since the 1970 epoch) representing the last time the Servlet or related data was updated. This information is used by caches.

HttpServlet Methods

Table 12.2 includes all of the methods for the javax.servlet.http. HttpServlet class, and example usage in Jython. Those methods that are overridden have a def statement in Jython, and those methods invoked on the superclass begin with self. Java signatures in Table 12.2 do not include return values or permission modifiers. For permissions, all methods are protected except for service(ServletRequest req, ServletResponse res), it has no permissions modifier (package private). All return values are void except for getLastModified, which returns a long type representing milliseconds since the epoch.

Table 12.2 HttpServlet Methods

Java Signature

Usage in Jython Subclass

doDelete(HttpServletRequest req, HttpServletResponse resp)

def doDelete(self, req, res):

doGet(HttpServletRequest req, HttpServletResponse resp)

def doGet(self, req, res):

doHead(HttpServletRequest req, HttpServletResponse resp)

def doHead(self, req, res):
*in J2EE version 1.3

doOptions(HttpServletRequest req, HttpServletResponse resp)

def doOptions(self, req, res):

doPost(HttpServletRequest req, HttpServletResponse resp)

def doPost(self, req, res):

doPut(HttpServletRequest req, HttpServletResponse resp)

def doPut(self, req, res):

doTrace(HttpServletRequest req, HttpServletResponse resp)

def doTrace(self, req, res):

getLastModified(HttpServletRequest req)

def getLastModified(self, req):

service(HttpServletRequest req, HttpServletResponse resp)

def service(self, req, res):

service(ServletRequest req, ServletResponse res)

def service(self, req, res):


The service method that accepts HTTP-specific request and response object redispatches those request to the appropriate do* method (if it isn't overridden). The service method that accepts a generic Servlet request and response object redispatches the request to the HTTP-specific service method. The redispatching makes the service methods valuable when implementing Servlet mappings.

HttpServlet Example

Listing 12.4 demonstrates a Servlet that subclasses javax.servlet.http. HttpServlet. The example implements both the doGet and doPost methods to demonstrate how those methods are called depending on the type of request received from the client. A web client should not allow a POST operation to be repeated without confirmation. Because of this, database updates, order commits, and so on should be in a doPost method, whereas the forms to do so can reside safely in a doGet.

The get_post.py file in Listing 12.4 shows how to get parameter names and values by using the HttpServletRequest's (req) getParameterNames and getParameterValues. The list returned from getParameterNames is a java.util.Enumeration, which the doPost method uses to display the request's parameters. Jython lets you use the for x in list: syntax with the enumeration returned from getParameternames. Note that the getParameterValues() method is plural. Parameters can have multiple values as demonstrated in the hidden form fields. To prevent redundancy in the doGet and doPost methods of the Servlet, Listing 12.4 adds a _params method. This method is not defined anywhere in HttpServlet or its bases, and because no Java class calls it, no @sig string is required. The purpose of the method is merely to keep similar operations in only one place.

Listing 12.4 Implementing a Servlet with HttpServlet

#file get_post.py
from time import time, ctime
from javax import servlet
from javax.servlet import http

class get_post(http.HttpServlet):
  head = "<head><title>Jython Servlets</title></head>"
  title = "<center><H2>%s</H2></center>"

  def doGet(self,req, res):
    res.setContentType("text/html")
    out = res.getWriter()

    out.println('<html>')
    out.println(self.head)
    out.println('<body>')
    out.println(self.title % req.method)

    out.println("This is a response to a %s request" %
          (req.getMethod(),))
    out.println("<P>In this GET request, we see the following " +
          "header variables.</P>")

    out.println("<UL>")
    for name in req.headerNames:
      out.println(name + " : " + req.getHeader(name) + "<br>")
    out.println("</UL>")

    out.println(self._params(req))
    out.println("""
      <P>The submit button below is part of a form that uses the
        "POST" method. Click on this button to do a POST request.
      </P>""")

    out.println('<br><form action="get_post" method="POST">' +
          '<INPUT type="hidden" name="variable1" value="one">' +
          '<INPUT type="hidden" name="variable1" value="two">' +
          '<INPUT type="hidden" name="variable2" value="three">' +
          '<INPUT type="submit" name="button" value="submit">')

    out.println('<br><font size="-2">time accessed: %s</font>'
          % ctime(time()))
    out.println('</body></html>')
   
  def doPost(self, req, res):
    res.setContentType("text/html");
    out = res.getWriter()

    out.println('<html>')
    out.println(self.head)
    out.println('<body>')
    out.println(self.title % req.method)

    out.println("This was a %s<br><br>" % (req.getMethod(),))
    out.println(self._params(req))
    out.println('<br> back to <a href="get_post">GET</a>')
    out.println('<br><font size="-2">time accessed: %s</font>'
          % ctime(time()))
    out.println('</body></html>')

  def _params(self, req):
    params = "Here are the parameters sent with this request:<UL>"
    names = req.getParameterNames()

    if not names.hasMoreElements():
      params += "None<br>"
    for name in names:
      value = req.getParameterValues(name)		
      params += "%s : %r<br>" % (name, tuple(value))
    params += "</UL>"
    return params

After placing the get_post.py file in the $TOMCAT_HOME/webapps/jython/WEB-INF/classes directory, compile it with the following:

jythonc –w . ––deep get_post.py

To test it, point your browser at http://localhost:8080/jython/servlet/get_post. You should see a browser window similar to that in Figure 12.1.

Figure 12.1. The GET view from get_post.py.

The parameters for the GET operation are None in this example, but test other parameters in the doGet method by adding some to the end of the URL(such as http://localhost:8080/servlet/get_post?variable1=1&variable2=2).

Because the Submit button on the bottom of the first view is part of a form implemented as a POST, clicking on the Submit button executes the doPost method of the same Servlet. The results of the doPost method should match what is shown in Figure 12.2.

Figure 12.2. The POST view from get_post.py.

HttpServletRequest and HttpServletResponse

Communication with client connections happens through the HttpServletRequest and HttpServletResponse object. These are abstractions of the literal request stream received from and sent to the client. Each of these objects adds higher-level, HTTP-specific methods to ease working with requests and responses.

Table 12.3 is a list of the methods within the HttpServletRequest object. A great majority of the methods are bean property accessors, which means that you can reference them with Jython's automatic bean properties. This may not be as great of an advantage here as it is for GUI programming because there is no opportunity to leverage this facility in method keyword arguments. Table 12.3 shows the methods and bean property names from the HttpServletRequest object.

Table 12.3 HttpServletRequest Methods and Properties

Method and Property

Description

Method: getAuthType()
Property name: AuthType

Returns a string (PyString) describing the name of the authentication type. The value is None if the user is notauthenticated.

Method: getContextPath()
Property name: contextPath

Returns a string (PyString) describing the path information that identifies the context requested.

Method: getCookies()
Property name: cookies()

Returns all cookies sent with the client's request as an array of j avax.servlet.http.Cookie objects.

Method: getDateHeader(name)

Retrieves the value of the specified header as a long type.

Method: getHeader(name)

Returns the value of the specified header as string (PyString).

Method: getHeaderNames()
Property name: headerNames

Returns all the header names contained within the request as an Enumeration.

Method: getHeaders(name)

Returns all the values of the specified header name as an Enumeration.

Method: getIntHeader(name)

Retrieves the specified header value as a Java int, which Jython converts to a PyInteger.

Method: getMethod()
Property name: method

Returns the type of request made a string.

Method: getPathInfo()
Property name: pathInfo

All extra path information sent by the client.

Method: getPathTranslated()
Property name: pathTranslated

Returns the real path derived from extra path information in the client's request.

Method: getQueryString()
Property name: queryString

Returns the query string from the client's request (the string after the path).

Method: getRemoteUser()
Property name: remoteUser

Returns the login name of the client. None if the client is not authenticated.

Method: getRequestedSessionId() Property name: requestedSessionId

Returns the clients session ID.

Method: getRequestURI()
Property name: requestURI

Returns that segment of the between the protocol name and the query string.

Method: getServletPath()
Property name: servletPath

Returns the portion of the URL that designates the current Servlet.

Method: getSession()
Property name: session

Returns the current session, or creates on if needed. A session is an instance of javax.servlet.http.HttpSession.

Method: getSession(create)

Returns the current session if one exists. If not, a new session is created if the create value is true.

Method: getUserPrincipal()
Property name: userPrincipal

Returns a java.security.Principal object with the current authentication information userPrincipal

Method: isRequestedSessionIdFromCookie()

Returns 1 or 0 depending on whether the current session ID was from a cookie.

Method: isRequestedSessionIdFromURL()

Returns 1 or 0 depending on whether the current session ID was from the requested URL string.

Method: isRequestedSessionIdValid()

Returns 1 or 0 depending on whether the requested session ID is still valid.

Method: isUserInRole(role)

Returns 1 or 0 indicating whether the user is listed in the specified role.


The HttpServletResponse object is used to send the mime-encoded stream back to the client. HttpServletResponse defines additional HTTP-specific methods that do not exist in a generic ServletResponse object. The methods within the HttpServletResponse object appear in Table 12.4. Whereas Jython adds numerous automatic bean properties to the HttpServletRequest object, the HttpServletResponse object only has one: status.

Table 12.4 HttpServletResponse Methods and Properties

Method and Property

Description

addCookie(cookie)

Add a cookie to the response.

addDateHeader(headerName, date)

Adds a header name with a date (long) value.

addHeader(headerName, value)

Adds a header name and value.

addIntHeader(headerName, value)

Adds a header name with an integer value.

containsHeader(headerName)

Returns a 1 or 0 depending whether the specified header.

encodeRedirectUrl(url)

Encodes a URL for the sendRedirect method. For version 2.1 and greater, use encodeRedirectURL instead.

encodeRedirectURL(url)

Encodes a URL for the sendRedirect method.

encodeURL(url)

Encodes a URL by including the session ID in it.

sendError(sc)

Sends an error using the status code.

sendError(sc, msg)

Sends an error using the specified status code and message.

sendRedirect(location)

Sends a temporary redirect to the specified location.

setDateHeader(headerName, date)

Sets a header name to the specified date (long) value.

setHeader(headerName, value)

Sets a header name to the specified value.

setIntHeader(headerName, value)

Sets a header name to the specified integer value.

setStatus(statusCode) status

Sets the response status code.


The HttpServletResponse class also contains fields that correspond to the standard HTTP response codes. You can use these with sendError(int) and setStatus(int). Table 12.5 lists the number and error code for these status codes.

Table 12.5 HttpServletResponse Status CodesError

Code

Status

100

SC_CONTINUE

101

SC_SWITCHING_PROTOCOLS

200

SC_OK

201

SC_CONTINUE

202

SC_ACCEPTED

203

SC_NON_AUTHORITATIVE_INFORMATION

204

SC_NO_CONTENT

205

SC_RESET_CONTENT

206

SC_PARTIAL_CONTENT

300

SC_MULTIPLE_CHOICES

301

SC_MOVED_PERMANENTLY

302

SC_MOVED_TEMPORARILY

303

SC_SEE_OTHER

304

SC_NOT_MODIFIED

305

SC_USE_PROXY

400

SC_BAD_REQUEST

401

SC_UNAUTHORIZED

402

SC_PAYMENT_REQUIRED

403

SC_FORBIDDEN

404

SC_NOT_FOUND

405

SC_METHOD_NOT_ALLOWED

406

SC_NOT_ACCEPTABLE

407

SC_PROXY_AUTHENTICATION_REQUIRED

408

SC_REQUEST_TIMEOUT

409

SC_CONFLICT

410

SC_GONE

411

SC_LENGTH_REQUIRED

412

SC_PRECONDITION_FAILED

413

SC_REQUEST_ENTITY_TOO_LARGE

414

SC_REQUEST_URI_TOO_LONG

415

SC_UNSUPPORTED_MEDIA_TYPE

500

SC_INTERNAL_SERVER_ERROR

501

SC_NOT_IMPLEMENTED

502

SC_BAD_GATEWAY

503

SC_SERVICE_UNAVAILABLE

504

SC_GATEWAY_TIMEOUT

505

SC_HTTP_VERSION_NOT_SUPPORTED


PyServlet

A great advantage of HttpServlet programming for Jython programmers is that the Jython distribution comes with the Servlet org.python.util. PyServlet. This Servlet loads, executes and caches Jython files so that you can write and view a Jython Servlet without any compiling step in the middle. This works with a Servlet mapping. A Servlet mapping is an association between a specific Servlet, PyServlet in this case, and a certain URL pattern, such as *.py. With a proper web.xml file, Jython's PyServlet class is mapped to all *.py files, so as requests for *.py files arrive, the PyServlet class loads, caches, and invokes methods in the *.py files as needed to respond.

PyServlet's design creates some restrictions on the Jython files it serves. A Jython file must contain a class that subclasses javax.servlet.http. HttpServlet. The name of that class must match the name of the file without the .py extension. In other words, the file Test.py must contain the class Test, which must be a subclass of javax.servlet.http.HttpServlet. Additionally, it is unsafe to use module-global identifiers other than the one class which subclasses HttpServlet.

A note on naming: With Servlets, PyServlet, and Jython modules that implement servlets, names get a bit confusing. To clarify any confusion, the term servlet appears in a generic sense regardless of which language implements it. PyServlet refers to the specific Servlet org.python.util.PyServlet. The confusion arises with those Jython modules that PyServlet actually serves. Files matching the *.py pattern within the context that are loaded, executed, and cached by PyServlet, need a name of their own to distinguish them. There is no precedence for any such term, but dubbed jylets they are for the sake of this chapter.

Installing PyServlet

All that is required to install the PyServlet is to define a Servlet mapping and make sure the jython.jar file is in the context's lib directory. A Servlet mapping is defined in the context's deployment descriptor file: web.xml. The web.xml file for the Jython context belongs in the location $TOMCAT_HOME/ webapps/jython/WEB-INF/web.xml. In Tomcat, the default web.xml file from $TOMCAT_HOME/conf/web.xml is used for all the settings that are not explicitly included in the context's web.xml. Therefore, there may not be a web.xml file in your context yet, and you need not define all properties in that context because default values already exist.

Listing 12.5 is an example deployment descriptor for the Jython context, which establishes PyServlet as the handler for context requests matching the *.py pattern. The essential additions to the web.xml file are a Servlet definition for PyServlet and a Servlet mapping that associates URLs with the PyServlet. Because Tomcat has default values for items not defined, Listing 12.5 is a sufficiently complete web.xml file for the Tomcat server.

Listing 12.5 A Sample Deployment Descriptor for PyServlet

<web-app>
  <servlet>
    <servlet-name>PyServlet</servlet-name>
    <servlet-class>
      org.python.util.PyServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>PyServlet</servlet-name>
    <url-pattern>*.py</url-pattern>
  </servlet-mapping>
</web-app>

The Servlet definition defines the Servlet name and the Servlet's class. Listing 12.5 defines the name as PyServlet and the class as org.python.util.PyServlet. The class is the full package-class hierarchy that uniquely identifies the Servlet class: PyServlet. The actual class resides in the jython.jar file, which should be in the context's lib directory.

The Servlet definition for PyServlet may optionally include initialization parameters (init-params). PyServlet uses such parameters in the initialization of Jython; therefore, this is where you would place any of the Jython property settings such as python.home, python.path, python.respectJavaAccessibility or any of the other properties normally set in Jython's registry file. Listing 12.6 is a web.xml file supplies a python.home and python.path value as init-params.

Listing 12.6 PyServlet init-params in web.xml

<web-app>
  <servlet>
    <servlet-name>PyServlet</servlet-name>
    <servlet-class>
      org.python.util.PyServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
      <param-name>python.home</param-name>
      <param-value>c:\jython-2.1</param-value>
    </init-param>
    <init-param>
      <param-name>python.path</param-name>
      <param-value>
        c:\jython-2.1\lib\site-packages
      </param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>PyServlet</servlet-name>
    <url-pattern>*.py</url-pattern>
  </servlet-mapping>
</web-app>

Properties that define locations of Jython resources, such as python.home and python.path are of special concern. These affect whether a context is self contained and affect cross-platform portability. If the python.home property points outside the context, or if the python.path property includes a directory outside the context, the context is no longer self contained. Additionally, the property values must be platform-specific paths. Notice the python.home and python.path properties in Listing 12.6. They would not work on other platforms, and there is no mechanism to create platform-neutral paths for these property values. Fortunately, PyServlet has a default value for the python.home property that creates both a self-contained and a platform-neutral context.

The default python.home value for PyServlet is the context's lib directory. This makes the default python.home value for the Jython context one of the following (depending on platform).

%TOMCAT_HOME\webapps\jython\WEB-INF\lib
$TOMCAT_HOME/webapps/jython/WEB-INF/lib

Additionally, Jython's lib directory automatically becomes one of the following:

%TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib
$TOMCAT_HOME/webapps/jython/WEB-INF/lib/Lib

This keeps all of Jython's resources within the context, keeping it self contained. The additional advantage is that PyServlet adds the default python.home path in a platform-independent manner, making those contexts that use the default value platform independent.

Testing PyServlet

You con confirm that the Servlet mapping is working by starting Tomcat and viewing a simple jylet (Jython-Servlet) in a browser. Below is a simple test jylet:

# File ServletMappingTest.py
from javax.servlet.http import HttpServlet

class ServletMappingTest(HttpServlet):
  def doGet(self, req, res):
    out = res.writer
    res.contentType = "text/html"
    print >> out, "Greetings from a jylet."

Save this test file as %TOMCAT_HOME%\webapps\jython\ServletMappingTest.py, then point your browser to http://localhost:8080/jython/ServletMappingTest.py. If you see the greeting message, the PyServlet mapping is correct. To complete the installation, create Jython's lib directory %TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib. This is the directory where you will place any Jython modules used in your jylets. After that, installation is complete.

Cookies

Cookies allows storage on the client's machine information that is to be included in subsequent requests. To create and manipulate cookies from Jython, use the javax.Servlet.http.Cookie class. Creating a new cookie requires instantiating javax.servlet.http.Cookie with a name and value as parameters. If you were using cookies to track book purchases, you could set the author and title with a cookie like this:

from javax.servlet import http
name = "book1"
value = "Lewis Carroll, Alice In Wonderland"
MyNewCookie = http.Cookie(name, value)

Now you have a new cookie with a name of book1, the value is the Rev. Dodgson's pen name, and the title Alice in Wonderland. To send this cookie to the client you need to use HttpServletResponse's addCookie(cookie) method:

res.addCookie(MyNewCookie)

Adding cookies should take place before sending other content through the response stream.

Cookies often require more attention than that, thus the cookie instance has methods to set the specifics of the cookie. Each cookie instance can use get and set methods or Jython's automatic bean properties for each of the following properties:

Listing 12.7 makes use of the cookie object's automatic bean properties to create and read cookies defined in a web form.

Listing 12.7 Cookies with Jython

# File: cookies.py
from javax import servlet
from javax.servlet import http

class cookies(http.HttpServlet):
  def doGet(self, req, res):
    res.setContentType("text/html")
    out = res.getOutputStream()

    print >>out, "<html><head><title>Cookies with Jython</title></head>"
    print >>out, """
      <body>\n<H2>Cookie list:</H2>
      Remember, cookies must be enabled in your browser.<br><br>"""

    # Here's the list of Cookies
    for c in req.cookies:
      print >>out, """
        <b>Cookie Name</b>= %s &nbsp; 
        <b>Value</b>= %s<br><br>""" % (c.name,c.value)

    print >>out, """<br><br><br>
      <HR><P>Use this form to add a new cookie</P>
      <form action="cookies.py" method="POST">
      <P>Name:<br><INPUT type="text" name="name" size="30"></P>
      <P>Value:<br><INPUT type="text" name="value" size="30"></P>
      <P>Use the MaxAge field to set the cookie's time-to-expire.
        A value of "0" deletes the cookie immediately, a value of 
        "-1" saves the cookie until the browser exits, and
        any other integer represents seconds until it expires 
        (i.e.- using "10" would expire 10 seconds after being set).</P>
      <P>MaxAge:<br><INPUT type="text" name="maxAge" size="30"></P>
      <INPUT type="submit" name="button" value="submit">
      \n</body>
      \n</html>
      """

  def doPost(self, req, res):
    res.setContentType("text/html");
    out = res.getWriter()
    name = req.getParameterValues("name")[0]
    value = req.getParameterValues("value")[0]
    maxAge = req.getParameterValues("maxAge")[0]
    
    if name:
      newCookie = http.Cookie(name, value)
      newCookie.maxAge = int(maxAge or -1)
      newCookie.comment = "Jython test cookie"
      res.addCookie(newCookie)

      print >>out, """
        <html><body>Cookie set successfully\n\n
        <P>click <a href="cookies.py">here</a>
        to view the new cookie.</P>
        <P>If cookies are enabled in your
        browser that is.</P>
        \n</body>
        \n</html>"""
	else:
      print >>out, """
        <html>\n<body>
        Cookie not set
        <P>No cookie "Name" provided</P>
        <P>click <a href="cookies">here</a>
        to try again</P>
        \n</body>
        /n</html>"""

To test the jylet in Listing 12.7, first make sure your browser is set to allow cookies. Then place the cookies.py file in the directory %TOMCAT_HOME%\webapps\jython and point your browser to http://localhsot:8080/jython/cookies.py. You should only see a heading and a form on your first visit to this servlet. Proceed to add a form entry—possibly "Jython Cookie" for the name and "My first Jython cookie" for the value, and click the Submit button (maxAge is optional). You should see confirmation that adding the cookie was successful. To confirm that the cookie was truly added, return to doGet method and see if it shows up in the cookie list. Figure 12.3 shows what a browser might display after returning to the doGet method. Note that Figure 12.3 assumes certain cookie names and values were entered, and your result page will depend on the names and values you use.

Figure 12.3. Defined Cookies with cookies.py.

Sessions

Cookies are the most common means of creating sessions with clients. Sessions are a means of tracking client information through a series of requests. The cookie example (see Listing 12.7) could have been used to store a sessions ID, but the Java HttpSession class makes session tracking much easier.

To create a session, use HttpRequest's getSession() method. This returns a HttpSession instance. HttpSession is a simple interface to the more complicated behavior of a session management subsystem. Using the HttpSession object from Jython differs only from Java in the syntax and the automatic bean properties for the object's get* methods.

Listing 12.8 creates a session object with the simple req.session bean property. Listing 12.8 allows the cookie or the URL to contain the session value. Because cookies may store the session value, you should use the req.session bean property or the req.getSession() method before sending other data to the output stream. If the client has cookies disabled, however, the session object still works because all URLs are rewritten with the encodeUrl method. Actually, it is just one URL that needs rewritten, and that is the form action. If there were any other URLs, they would also necessarily go through the res.encodeUrl() method to support cookie-less sessions.

Once you have a HttpSession instance, passing data through the session is just a matter of using key, value pairs with putValue(key, value) and value = getValue(key).

Listing 12.8 Session Management

# File: session.py
from javax import servlet
from javax.servlet import http

class session(http.HttpServlet, servlet.RequestDispatcher):
  def doGet(self, req, res):
    sess = req.session
    res.contentType = "text/html"
    out = res.getWriter()

    name = req.getParameterValues("name")
    value = req.getParameterValues("value")
    if name and value:
      sess.putValue(name[0], value[0])

    print >>out, ("""
      <html>
      <body>
      <H3>Session Test</H3>
      Created at %s<br>
      Last Accessed = %s<br>
      <u>Values:</u>""" % 
      (sess.creationTime, sess.maxInactiveInterval)
    )

    print >>out, "<ul>"
    for key in sess.getValueNames():
      print >>out, "<li>%s: %s</li>" % (key, sess.getValue(key))
    print >>out, "</ul>"

    print >>out, """
      <HR><P>Use this form to add a new values to the session</P>
      <form action="session.py" method="GET">
      <P>Name:<br><INPUT type="text" name="name" size="30"></P>
      <P>Value:<br><INPUT type="text" name="value" size="30"></P>
      <INPUT type="submit" name="button" value="submit">
      </body>
      </html>
      """

After saving the session.py file from Listing 12.8 in the %TOMCAT_HOME%\ webapps\jython directory, point your browser to http://localhost:8080/jython/session.py. Use the web form to add some variables to the session to confirm it is working, and even try disabling cookies to see how the URL rewriting works. Figure 12.4 is the session.py results after adding a series of arbitrary values (results depend on the values added).

Figure 12.4. Session variables with session.py.

Databases and Servlets

Connecting to a database, executing statements and traversing result sets is no different in a jylet than it is in other Jython database applications. Managing connections and other database resources, however, is a primary concern because many web applications contain numerous jylets that use database content. Creating a database connection in each jylet quickly consumes the database resources, which has bad consequences. Alternatively, creating and closing a database connection for each request is unacceptable overhead.

There are many options for managing database resources, but two primary approaches are a connection per jylet, or connection pooling. Using a connection for each jylet is a popular but resource intensive approach. This approach involves establishing a database connection in the jytlet's init method, and closes that connection only when they jylet is unloaded. This eliminates any connection overhead while responding to client requests. However, this is only reasonable if the number of connections you require is well within resource limits. Most situations call for more prudent resource management.

Listing 12.9 implements a jylet that obtains a database connection and cursor object in its init method, which are closed in its destroy method. Therefore, the jylet incurs the connection overhead only at initialization, not during each client request. The database implementation in Listing 12.9 uses the zxJDBC package and MySQL database described in Chapter 11, "Database Programming." You must therefore include the classes required for MySQL and zxJDBC in the context's lib directory. For Listing 12.9, the mm_mysql-2_0_4-bin.jar file and the zxJDBC.jar file should be placed in the directory %TOMCAT_HOME%\webapps\jython\WEB-INF\lib. You should restart Tomcat after adding jar files to the lib directory to ensure it detects the new jar files.

Listing 12.9 Jython Servlet with Database Connection

# file: DBDisplay.py
from javax.servlet import http
from com.ziclix.python.sql import zxJDBC

class DBDisplay(http.HttpServlet):
  def init(self, cnfg):
    #define the JDBC url
    url = "jdbc:mysql://192.168.1.77/products"
    usr = "productsUser"   # replace with real user name 
    passwd = "secret"    # replace with real password
    driver = "org.gjt.mm.mysql.Driver"

    #connect to the database and get cursor object
    self.db = zxJDBC.connect(url, usr, passwd, driver)
    self.c = self.db.cursor()

  def doGet(self, req, res):
    res.setContentType("text/html")
    out = res.getWriter()

    print >>out, """
      <html>
      <head>
       <title>Jylet Database Connection</title>
      </head>
      <body>
      <table align="center">
       <tr>
        <td><b>ID</b></td>
        <td><b>Title</b></td>
        <td><b>Description</b></td>
        <td><b>Price</b></td>
       </tr>"""

    self.c.execute("select code, name, description, price from products")
    for row in self.c.fetchall():
      print >>out, """
        <tr>
         <td>%s</td>
         <td>%s</td>
         <td>%s</td>
         <td>%s</td>""" % row

    print >>out, """
      </table>
      </body>
      </html>"""

  def destroy(self):
    self.c.close()
    self.db.close()

Listing 12.9 assumes there is a database named products that includes the products table, and that the products table has at least the fields code, name, description, and price. To create such a database, use the following SQL statement:

create database products

To create the products table, use the following SQL statements:

CREATE TABLE products (
 primarykey int(11) NOT NULL auto_increment,
 code varchar(55) default NULL,
 name varchar(255) default NULL,
 description text,
 price float(5,2) default NULL,
 PRIMARY KEY (primarykey)
) TYPE=MyISAM;

After creating the database and table, creating some arbitrary values for each of the fields and placing the DBDisplay.py file in the context's root directory, you should be able to point your browser to http://localhost:8080/jython/DBDisplay.py to see the database data.

If your web application begins using excessive connections, consider instead using connection pooling. Connection pooling allows for both prudent resource management and elimination of connection overhead. A connection pool maintains a certain number of active database connections that jylets borrow when replying to client requests and return to the pool when done. This creates a predictable, static number of connections. It is also possible to use a statement pool, giving statements the same advantage. A popular, free, tested, and well-documented connection-pooling tool is PoolMan from http://www.codestudio.com/. Other Java connection-pooling packages exist as well, and all should work seamlessly with Jython.

JSP

Java Server Pages (JSP) is the templating system espoused by Sun as a compliment to Servlets. A JSP file contains a web page's markup code and text unaltered, but also contains special tags that designate dynamic content. Currently, Tomcat only implement the Java language in JSP, so the big question is how do you use Jython with JSP. Currently, the answer is that you do not use Jython directly. You cannot include a code tag (<% code %>) where the code is in the Jython language. What you can do is use jythonc-compiled classes, use an embedded PythonInterpreter, or create a Jython-specific, custom tag library.

jythonc-Compiled Classes and JSP

Using jythonc-compiled classes with JSP requires creating a Java-compatible Jython class. One that has the same name as the module within it, inherits from a Java class, and includes @sig strings for each method not derived from the superclass. Placing class files generated with jythonc in the context's classes directory allows JSP pages to use those classes just like any native Java class. Of course, this also requires that the jython.jar file is placed in the context's lib directory.

A JSP file can use a jythonc-compiled class one of two ways, in scriptlets, or as a bean. If it is to be used as a bean, the Jython class must comply with bean conventions and include a getProperty and setProperty method for each read-write property. A scriptlet, however, can use any class. Whether as a bean or in a scriptlet, you must first load the jythonc-compiled class into the appropriate scope. To import a non-bean class, use the page import directive:

<%@ page import="fully.qualified.path.to.class" %>

For a bean, use the jsp:useBean tag to load the bean:

<jsp:useBean name="beanName"
       class="fully.qualified path.to.class"
       scope="scope(page or session)">

To use a non-bean class, include Java code using that class in scriplet tags (<% %>) or in expression tags (<%= %>). If you imported the hypothetical class ProductListing, you could use that class in something similar to the following contrived JSP page:

<%@ page import="ProductListing" %>

<html>
<body>

<!––The next line begins the scriptlet ––>
<% ProductListing pl = new ProductListing(); %>

<table>
 <tr>
  <td><%= pl.productOne %></td>
  <td><%= pl.productTwo %></td>
 </tr>
</table>

Scriplets are often discouraged because they complicate the JSP page. The preferred implementation uses jythonc-compiled beans along with the JSP useBean, setProperty, and getProperty tags. Listing 12.10 is a very simple bean written in Jython that stores a username. It will serve as an example on how to use a bean written in Jython.

Listing 12.10 A Simple Bean Written in Jython

# file: NameHandler.py
import java

class NameHandler(java.lang.Object):
  def __init__(self):
    self.name = "Fred"
  def getUsername(self):
    "@sig public String getname()"
    return self.name
  def setUsername(self, name):
    "@sig public void setname(java.lang.String name)"
    self.name = name

Place the nameHandler.py file from Listing 12.10 in the %TOMCAT_HOME%\webapps\jython\WEB-INF\classes directory and compile it from within that directory with the following command:

jythonc –w . Namehandler.py

Listing 12.11 is a simple JSP page that uses the NameHandler bean.

Listing 12.11 Using a Jython Bean in a JSP page

<!––file: name.jsp ––> 
<%@ page contentType="text/html" %>

<jsp:useBean id="name" class="NameHandler" scope="session"/>

<html>
<head>
 <title>hello</title>
</head>
<body bgcolor="white">
Hello, my name is

<jsp:getProperty name="name" property="username"/>
<br>

No, wait...

<jsp:setProperty name="name" property="username" value="Robert"/>

, It's really <%= name.getUsername() %>.

</body>
</html>

Notice that the jsp:setProperty does the same as the <%= beanName.property = value %> expression and the jsp:getProperty is the same as the <%= beanName.property %> expression.

To use the JSP page in Listing 12.11, place the name.jsp file in the context's root directory (%TOMCAT_HOME%\webapps\jython), and then point your browser to http://localhost:8080/jython/name.jsp. You should see the default name Fred and the revised name Robert.

Embedding a PythonInterpreter in JSP

If you did wish to use Jython code within a JSP scriptlet, you could indirectly do so with a PythonInterpreter instance. This would require that you use an import directive to import org.python.util.Pythoninterpreter. Listing 12.12 shows a simple JSP page that uses the PythonInterpreter object to include Jython code in JSP pages.

Listing 12.12 Embedding a PythonInterpreter in a JSP Page

<!––name: interp.jsp––>
<%@ page contentType="text/html" %>
<%@ page import="org.python.util.PythonInterpreter" %>

<% PythonInterpreter interp = new PythonInterpreter();
  interp.set("out, out); %>


<html>
<body bgcolor="white">
<% interp.exec("out.printIn('Hello from JSP and the Jython interpreter.')"); %>
</body>
</html>

To use the interp.jsp file, make sure that the jython.jar file is in the context's lib directory, and place the interp.jsp file in the context's root directory (%TOMCAT_HOME%\webapps\jython). If you then point your browser at http://localhost:8080/jython/interp.jsp you should see the simple message from the Jython interpreter.

Note that many consider scriptlets poor practice anyway, so adding another level of complexity by using Jython from Java in scriptlets is obviously suspect. There are better ways to create dynamic content, such as bean classes and taglibs.

A Jython Taglib

Taglibs are custom tag libraries that you can use within JSP pages. You can create taglibs in Jython by using jythonc to compile Jython taglib modules into Java classes. A Jython taglib module must then comply with certain restrictions before it transparently acts as a Java class. The module must contain a class with the same name as the module (without the .py extension). The class must subclass the javax.servlet.jsp.tagext.Tag interface or a Java class that implements this interface. The org.python.* packages and classes, and Jython library modules (if the taglib imports any) must be accessible to the compiled class file.

To make all the required classes and libraries available to a taglib, you can compile it with jythonc's ––all option much like was done with jythonc-compiled Servlets earlier in this chapter. Doing so would create a jar file that you would then place in the context's lib directory. The problem is that it is very likely that numerous resources will use the Jython core files, so repeatedly compiling with ––core, ––deep, or ––all creates wasteful redundancy. The better plan is to include the jython.jar file in the context's lib directory, establish a Jython lib directory for Jython's modules somewhere in the context ({context}\WEB-INF\lib\Lib is recommended—see the earlier section "PyServlet" for an explanation), and then compile the Jython taglib modules without dependencies.

Listing 12.13 is a simple Jython taglib module that serves as the specimen used to dissect taglibs. All Listing 12.13 does is add a message, but it implements all the essential parts of a taglib. The basic requirements implemented in Listing 12.13 are as follows:

Listing 12.13 A Jython Taglib

# file: JythonTag.py
from javax.servlet.jsp import tagext

class JythonTag(tagext.Tag):
  def __init__(self):
    self.context = None
    self.paren = None

  def doStartTag(self):
    return tagext.Tag.SKIP_BODY

  def doEndTag(self):
    out = self.context.out
    print >>out, "Message from a taglib"
    return tagext.Tag.EVAL_PAGE

  def release(self):
    pass

  def setPageContext(self, context):
    self.context = context

  def setParent(self, parent):
    self.paren = parent

  def getParent(self):
    return self.paren

The instance variables holding the pageContext and parent tag information (self.context and self.paren) deserve a special warning. These variables either must not be identified as self.pageContext and self.parent, or all access to these variables must go through the instance's __dict__. The Tag interface requires that implementations of the setPageContext(), setParent(), and getParent() methods, but because these methods exist, Jython creates an automatic bean property for their associated property names. This makes circular references and StackOverflowExceptions easy to make. Imagine the following code:

def setPageContext(self, context):
  self.context = context

The setPageContext method assigns the context to the instance attribute self.context. However, self.context is an automatic bean property, meaning that self.context really calls self.setPageContext(context). This type of circular reference is always a concern when using jythonc-compiled classes in Java frameworks, but it is the explicit requirement of get* and set* methods due to the interface that increases the likelihood here.

To install the classes required for the taglib in Listing 12.13, first ensure the jython.jar file is in the context's lib directory, then compile the JythonTag.py file with the following command:

jythonc –w %TOMCAT_HOME%\webapps\jython\WEB-INF\classes JythonTag.py

This will create the JythonTag.class and JythonTag$_PyInner.class files in the context's classes directory. The classes themselves are insufficient to use the taglib, however. You must also create a taglib library description file before using the tag in a JSP page. Listing 12.14 is a taglib library description file appropriate for describing the tag defined in Listing 12.13.

Listing 12.14 Tag Library Descriptor

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib
 PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
 "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

<taglib>
  <tlibversion>1.0</tlibversion>
  <jspversion>1.1</jspversion>
  <shortname>JythonTag</shortname>
  <info>A simple Jython tag library</info>
  <tag>
    <name>message</name>
    <tagclass>JythonTag</tagclass>
  </tag>
</taglib>

Place the tag library descriptor information from Listing 12.14 in the file %TOMCAT_HOME%\webapps\jython\WEB-XML\jython-taglibs.tld. JSP pages will use this file to identify tags used within the page.

The tag library descriptor identifies characteristics of the tag library such as its version number, and the version of JSP it is designed for. It also specifies a name <shortname> for referencing this tag library. The remaining elements define the one specific tag library created in listing 12.13. The tag element identifies the fully qualified class name and assigns a name to that class. The JythonTag is not in a package, so it is a fully qualified name on its own, and this class is associated with the name taglet.

With JythonTag compiled and the tag library descriptor saved, that remaining step is to use the tag library from a JSP page. A JSP page must include a directive that designates the path to the tag library descriptor and assigns a simple name that library. All subsequent references to tags within this library will begin with the name assigned in this directive. A JSP directive for the tag library created in Listings 12.13 and 12.14 would look like the following:

<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>

Remember that the .tld file specified the name message for our example tag, so after declaring this tag library, you may subsequently use the message tag with the following:

<jython:message/>

The jython portion of the tag comes from the name assigned in the JSP declaration, while the message portion comes from a tag class's assigned name as specified in the tag library description. Listing 12.15 is a JSP file that uses the tag defined in Listing 12.13 and the taglib descriptor in Listing 12.14. Save the test.jsp file in Listing 12.14 as %TOMCAT_HOME%\webapps\jython\test.jsp, then point your browser to http://localhost:8080/jython/test.jsp, and you should see the custom tag message.

Listing 12.15 A JSP File that Employs the JythonTag

<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
<html>
<body>

<jython:message />

</body>
</html>

Implementing taglibs with compiled jython modules again raises issues with Jython's home directory and Jython's lib directory. A jythonc-compiled taglib will not know where python.home is or where to find library modules unless that information is established in the PySystemState. You can set the python.home property in the TOMCAT_OPTS environment variable, but you could also leverage the PyServlet class to establish this system state information. If the context you are in loads the PyServlet class, then the python.home and sys.path information may already be correct for the taglibs that need it. With the default PyServlet settings, python.home is %TOMCAT_HOME%\webapps\ jython\WEB-INF\lib, and therefore Jython's lib directory is %TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib. With PyServlet loaded, taglibs may load Jython modules from the same lib directory.

BSF

IBM's Bean Scripting Framework (BSF) is a Java tool that implements various scripting languages. Jython is one of the languages that BSF currently supports. The Apache Jakarta project includes a subproject called taglibs, which is an extensive collection of Java tag libraries ready for use; however, the interesting taglib is the BSF tag library. The BSF tag library combined with the BSF jar file itself allows you to quickly insert Jython scriptlets and expression directly into JSP pages.

The BSF distribution currently requires a few tweaks to work with Jython. Because the BSF distribution will eventually include these small changes, there is no advantage to detailing them here; however, this does mean you need to confirm your version of BSF includes these changes. To make this easy, the website associated with this book (http://www.newriders.com/) will include a link to where you can download the correct bsf.jar file. After downloading the bsf.jar file, place it in the context's lib directory (%TOMCAT_HOME%\ webapps\jython\WEB-INF\lib). The website associated with this book will also contain download links for the BSF taglibs and bsf.tld files. Place the jar file containing the BSF taglibs in the context's lib directory, and place the bsf.tld file in the context's WEB-INF directory (%TOMCAT_HOME%\webapps\jython\WEB-INF\bsf.tld).

With these files installed, you can use Jython scriptlets within JSP files. You first must include a directive to identify the BSF taglib:

<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %>

Then you can use the bsf.scriptlet tag, including Jython code in the tag body like so:

<bsf.scriptlet language="jython">
import random
print >>, out random.randint(1, 100)
</bsf.scriptlet>

The scriptlet has a number of JSP objects automatically set in the interpreter, and these objects are listed below with their Java class names:

Most of the objects are familiar from Servlets, but the BSF scriptlet tag lets you use them within JSP pages. Listing 12.16 shows a JSP page that uses the bsf:scriptlet tag to create a timestamp in a JSP file.

Listing 12.16 BSF Scriptlet

<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %>
<html>
<body>

<center><H2>BSF scriptlets</H2></center>

<b>client info:</b><br>

<bsf:scriptlet language="jython">
for x in request.headerNames:
  print >>out, "%s: &nbsp;%s<br>\n" % (x, request.getHeader(x))
</bsf:scriptlet>

<br><br>
<b>Time of request:</b><br>

<bsf:scriptlet language="jython">
import time
print >>out, time.ctime(time.time())
</bsf:scriptlet>

</body>
</html>

After saving Listing 12.16 as the file %TOMCAT_HOME%\webapps\jython\scriptlets.jsp, point your browser to http://localhost:8080/jython/scriptlets.jsp. You should see all the client information as well as the access time.

800 East 96th Street, Indianapolis, Indiana 46240