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 subprocess 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 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.
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.
Listing 12.1 is the most basic of Java Servlets.
Example 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.
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.
Example 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 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.
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%instartup.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%inshutdown.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%webappsjython The context's root %TOMCAT_HOME%webappsjythonWEB-INF %TOMCAT_HOME%webappsjythonWEB-INFclasses Servlet classes %TOMCAT_HOME%webappsjythonWEB-INFlib 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 )
To install the Java Servlet from Listing 12.1, first place the JavaServlet.
java
file in the directory %TOMCAT_HOME%webappsjythonWEB-INFclasses
. 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%libservlet.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
.
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%jythonWEB-
INFclasses
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:
Compile the Jython Servlet module with jythonc
.
Add the jython.jar
file to the web application.
Make the modules from Jython’s lib
directory available to Jython Servlets.
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 %
TOM
CAT_HOME%jythonWEB-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%jythonWEB-INFclasses
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
INFclasses
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.
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
:
Add jython.jar
to the context’s lib
directory. This is the preferred way.
Add jython.jar
to Tomcat’s lib
directory. This method is discouraged.
Leave jython.jar
in Jython’s installation directory, but add it to the classpath
before running Tomcat. This is reasonable, but not as good as placing it in the context’s lib
directory.
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
jythonWEB-INFlib
. 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.
There are three ways to make the modules in Jython’s lib
directory available to Servlets:
Set the python.home
property.
Freeze required modules.
Have each Servlet explicitly append module locations to the sys.path
.
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%webappsjythonWEB-
INFjylib
. Because Jython looks for the lib
directory within python.home
, continue by also making the directory %
TOMCAT_HOME%webappsjython
WEB-INFjylib
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%webappsjythonWEB-INFjylib # 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.
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
.
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:
Methods overridden in the Jython subclass, noted by the def
statements.
Superclass methods invoked from the Jython subclass, noted by qualifying the method with self
.
Table 12.1. GenericServlet Methods
Java Signature |
Usage in Jython Subclass | |
---|---|---|
METHODS YOU SUBCLASS: | ||
| ||
|
| |
|
| |
| ||
| ||
|
| |
|
| |
|
| |
METHODS YOU USE WITH THE SELF. METHOD SYNTAX: | ||
|
| |
| ||
|
| |
| ||
|
| |
| ||
|
| |
| ||
|
|
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 HelloWorld
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.
Example 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%webappsjythonWEB-INFclasses
), 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%inshutdown.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.
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.
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.
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.
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.
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 |
---|---|
| |
|
|
| |
|
|
|
|
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
| |
|
|
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.
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.
Example 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.
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.
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:
Property name:
|
Returns a string ( |
Method:
Property name:
|
Returns a string ( |
Method:
Property name:
|
Returns all cookies sent with the client’s request as an array of |
Method:
|
Retrieves the value of the specified header as a long type. |
Method:
|
Returns the value of the specified header as string ( |
Method:
Property name:
|
Returns all the header names contained within the request as an Enumeration. |
Method:
|
Returns all the values of the specified header name as an Enumeration. |
Method:
|
Retrieves the specified header value as a Java |
Method:
Property name:
|
Returns the type of request made a string. |
Method:
Property name:
|
All extra path information sent by the client. |
Method:
Property name:
|
Returns the real path derived from extra path information in the client’s request. |
Method:
Property name:
|
Returns the query string from the client’s request (the string after the path). |
Method:
Property name:
|
Returns the login name of the client. None if the client is not authenticated. |
Method:
Property name:
|
Returns the clients session ID. |
Method:
Property name:
|
Returns that segment of the between the protocol name and the query string. |
Method:
Property name:
|
Returns the portion of the URL that designates the current Servlet. |
Method:
Property name:
|
Returns the current session, or creates on if needed. A session is an instance of |
Method:
|
Returns the current session if one exists. If not, a new session is created if the create value is true. |
Method:
Property name:
|
Returns a |
Method:
|
Returns |
Method:
|
Returns |
Method:
|
Returns |
Method:
|
Returns |
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 |
---|---|
|
Add a cookie to the response. |
|
Adds a header name with a date (long) value. |
|
Adds a header name and value. |
|
Adds a header name with an integer value. |
|
Returns a |
|
Encodes a URL for the |
|
Encodes a URL for the |
|
Encodes a URL by including the session ID in it. |
|
Sends an error using the status code. |
|
Sends an error using the specified status code and message. |
|
Sends a temporary redirect to the specified location. |
|
Sets a header name to the specified date (long) value. |
|
Sets a header name to the specified value. |
|
Sets a header name to the specified integer value. |
|
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 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.
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.
Example 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.
Example 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.1libsite-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_HOMEwebappsjythonWEB-INFlib $TOMCAT_HOME/webapps/jython/WEB-INF/lib
Additionally, Jython’s lib
directory automatically becomes one of the following:
%TOMCAT_HOME%webappsjythonWEB-INFlibLib $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.
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%webappsjythonServletMappingTest.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%webappsjythonWEB-INFlibLib
. This is the directory where you will place any Jython modules used in your jylets. After that, installation is complete.
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:
comment
domain
maxAge
name
path
secure
value
version
Listing 12.7 makes use of the cookie object’s automatic bean properties to create and read cookies defined in a web form.
Example 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> <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 <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"> </body> </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 <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> </body> </html>""" else: print >>out, """ <html> <body> Cookie not set <P>No cookie "Name" provided<</P> <P>click <a href="cookies">here</a> to try again</P> </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%webappsjython
and point your browser to http://localhost: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.
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)
.
Example 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%webappsjython
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).
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%webappsjythonWEB-INFlib
. You should restart Tomcat after adding jar
files to the lib
directory to ensure it detects the new jar
files.
Example 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.
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.
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.
Example 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%webappsjythonWEB-INFclasses
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.
Example 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%webappsjython
), and then point your browser to http://localhost:8080/jython/name.jsp
. You should see the default name Fred and the revised name Robert.
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.
Example 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%webappsjython
). 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.
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-INFlibLib
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:
The name of the file in Listing 12.13 is the same as the class it contains.
The class implements (subclasses) the javax.servlet.jsp.tagext.Tag
interface. Other options for the superclass (interfaces) include the BodyTag
and IterationTag
interfaces, and the TagSupport
or BodyTagSupport
classes from the javax.servlet.jsp.tagext
package.
The JythonTag
class implements all the methods required to complete the Tag
interface.
Example 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%webappsjythonWEB-INFclasses 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.
Example 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%webappsjythonWEB-XMLjython-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%webappsjython est.jsp
, then point your browser to http://localhost:8080/jython/test.jsp
, and you should see the custom tag message.
Example 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%webappsjythonWEB-INFlib
, and therefore Jython’s lib
directory is %TOMCAT_HOME%webappsjythonWEB-INFlibLib
. With PyServlet
loaded, taglibs may load Jython modules from the same lib
directory.
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%webappsjythonWEB-INFlib
). 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%webappsjythonWEB-INFsf.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.
Example 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: %s<br> " % (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%webappsjythonscriptlets.jsp
, point your browser to http://localhost:8080/jython/scriptlets.jsp
. You should see all the client information as well as the access time.