Jython在服务器端Web编程中的应用
在服务器端Web编程领域,Jython有着独特的应用价值。它可以在Java适用的任何地方发挥作用,尤其在那些需要快速开发和高灵活性的Java应用场景中表现出色。Java服务器端Web编程主要通过Servlet和Java Server Pages(JSP)实现,因此Jython的主要应用也是Servlet和JSP。不过,Java的企业级包(j2ee)在Web应用开发中也很重要,EJB、JNDI、JDBC等技术在大多数Java Web应用中不可或缺,Jython在这些技术方面同样有效。
1. Jython的适用与不适用场景
Jython并非适用于所有场景。CPython是实现CGI脚本的流行语言,但Jython并不适合。在CGI中,Web服务器接收请求后启动一个进程进行响应,响应完成后关闭该子进程。虽然可以用Jython这样做,但由于JVM的启动时间较长,这并不是一个好选择。而Servlet和JSP则具有持久性,它们在Web请求之间会保留在内存中。
使用Jython进行Web编程有很多优势。高质量的Servlet容器很容易获取且广泛部署。Java Servlet应用可以受益于Jython的灵活性和高级语言特性,同时还能利用Java和Jython的库。常见的Servlet容器有WebLogic、WebSphere、Tomcat、Jigsaw、Resin和Jetty等,许多组织都部署了这些容器,这使得Jython在很多情况下都能立即使用。
2. Jython Servlet容器
Jython可以与任何兼容的Java Servlet容器配合使用,有很多这样的容器可供选择。这里以Tomcat为例,它是Servlet和Java Server Page规范的参考实现。以下是一些流行且免费的Servlet容器介绍:
| 容器名称 | 描述 | 下载地址 | 支持规范 |
| — | — | — | — |
| Jakarta’s Tomcat | 是Servlet和Java Server Pages规范的参考实现 | http://jakarta.apache.org/ | 3.2.3版本支持2.2 Servlet和1.1 JSP规范;4.0版本实现2.3 Servlet和1.2 JSP规范 |
| Apache JServ | 为Apache创建的Servlet(版本2.0)引擎 | http://java.apache.org/ | 需要Java Servlet Development Kit 2.0,可从http://java.sun.com/products/servlet/index.html下载;Java Server Pages需要外部模块,位于http://java.apache.org/jserv/ |
| Jigsaw | W3C的实验性Web服务器,是完全符合HTTP/1.1的Web服务器和缓存代理服务器 | http://www.w3.org/Jigsaw/ | 支持Servlet 2.2规范和Java Server Pages 1.1 |
| Jetty | 紧凑高效的Java Web服务器 | http://jetty.mortbay.com/ | 支持Servlet 2.2和JSP 1.1规范,支持HTTP 1.1,包含SSL支持,可轻松与EJB服务器集成 |
这些工具的文档很丰富,安装指南可从各自的网站获取。
3. 定义简单的Servlet类
下面比较一个简单的Java Servlet和一个简单的Jython Servlet,并介绍如何在Tomcat Web应用中安装jython.jar文件。
3.1 简单的Java Servlet
以下是一个基本的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>");
}
}
这个示例使用了Servlet API中的GenericServlet类,
service
方法是抽象的,必须在子类中实现,它会在接收到Web请求时被调用,输出内容通过
PrintWriter
对象发送给客户端。
3.2 简单的Jython Servlet
对应的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>""")
这个Jython Servlet与Java Servlet类似,继承自
GenericServlet
并实现了
service
方法。不过,Jython的语法有一些不同,如类定义后的括号指定父类、省略
throws
语句、
service
方法有显式的
self
参数、没有分号和显式类型声明等。
4. 测试Java和Jython Servlet
测试这两个Servlet需要安装Tomcat,Jython Servlet还需要将jython.jar文件包含在Tomcat中。
4.1 安装Tomcat
安装Tomcat的步骤如下:
1. 从http://jakarta.apache.org下载Tomcat,建议下载jakarta-tomcat-3.2.3版本,根据平台选择合适的zip或tar.gz文件。
2. 将下载的文件解压到有足够权限的目录,该目录即为Tomcat主目录。设置环境变量
TOMCAT_HOME
指向该目录,例如在Windows上:
set TOMCAT_HOME=c:\jakarta-tomcat-3.2.3
在*nix系统上:
export TOMCAT_HOME=/usr/local/jakarta-tomcat-3.2.3
如果不设置该环境变量,则必须在Tomcat主目录或bin目录下启动Tomcat。
3. 设置环境变量
JAVA_HOME
指向JDK安装的根目录,例如使用JDK1.3.1时,在Windows上:
set JAVA_HOME=c:\jdk1.3.1
在*nix系统上:
export JAVA_HOME=/usr/java/jdk1.3.1
-
安装完成后,使用适合平台的启动脚本启动Tomcat:
在Windows上:
%TOMCAT_HOME%\bin\startup.bat
在*nix系统上:
$TOMCAT_HOME/bin/startup.sh
启动时,注意查看是否有如下信息:
date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080
这表示将使用8080端口连接到Servlet容器。
5. 停止Tomcat时,使用相应的关闭脚本:
在Windows上:
%TOMCAT_HOME%\bin\shutdown.bat
在*nix系统上:
$TOMCAT_HOME/bin/shutdown.sh
Servlet 2.2规范指定了Web应用的目录层次结构,从
%TOMCAT_HOME%\webapps
开始,该目录下的文件夹是Web应用或上下文,每个上下文都有特定的目录结构。这里使用名为
jython
的上下文,其目录结构如下:
%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
在继续示例之前,应创建这些目录。重启Tomcat时,若看到如下信息,则表示Tomcat已加载新的上下文:
date time - ContextManager: Adding context Ctx( /jython )
4.2 安装Java Servlet
安装Java Servlet的步骤如下:
1. 将
JavaServlet.java
文件放在
%TOMCAT_HOME%\webapps\jython\WEB-INF\classes
目录下,该目录是类文件的根目录。由于
JavaServlet
不在包中,所以放在
classes
目录的根目录下。
2. 在该目录下,使用以下命令编译
JavaServlet.java
文件:
javac -classpath %TOMCAT_HOME%\lib\servlet.jar JavaServlet.java
-
编译完成后,启动Tomcat服务器,然后在浏览器中访问
http://localhost:8080/jython/servlet/JavaServlet,应看到消息“This is a Java Servlet.”
4.3 安装Jython Servlet
使用Jython Servlet有两种方式:一种是使用jythonc编译Servlet并将生成的类文件放在
%TOMCAT_HOME%\jython\WEB-INF\classes
目录下;另一种是使用Jython的PyServlet映射类。这里使用jythonc,步骤如下:
1.
使用jythonc编译Jython Servlet模块
:
编译时,类路径中必须包含
servlet.jar
文件,该文件包含
javax.Servlet.*
类和包,位于Tomcat的
lib
目录(
%TOMCAT_HOME%\lib
)中。将
JythonServlet.py
文件放在
%TOMCAT_HOME%\jython\WEB-INF\classes
目录下,确保环境变量
CLASSPATH
包含
servlet.jar
,然后在该目录下使用以下命令编译Jython代码:
jythonc -w . JythonServlet.py
-w
开关指定当前工作目录,避免将生成的类文件从
jpywork
目录复制出来。编译后,
classes
目录中应有两个类文件,如
JythonServlet.java
、
JythonServlet.class
和
JythonServlet$_PyInner.class
,这两个类文件都必须在
WEB-INF\classes
目录中才能使用Servlet。编译时,要注意查看是否有如下信息:
Creating .java files:
JythonServlet module
JythonServlet extends javax.servlet.GenericServlet
如果没有看到这些信息,说明有问题,Servlet将无法工作,需检查
CLASSPATH
和设置选项,重新编译直到看到这些信息。
2.
将jython.jar添加到类路径
:
所有Jython Servlet都必须能够访问jython.jar文件中的类,有三种方法将jython.jar文件添加到Tomcat的类路径:
-
添加到上下文的lib目录
:这是首选方法,将jython.jar文件放在
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib
目录中,这样可以创建一个自包含的Web应用。
-
添加到Tomcat的lib目录
:不推荐这种方法,虽然可以减少jython.jar文件的重复,但Web应用不再自包含,且无法自动访问Jython的lib目录。
-
留在Jython的安装目录,但在运行Tomcat之前将其添加到类路径
:这种方法可以消除jython.jar文件的重复,还能访问注册表文件和Jython的lib目录,但自包含的上下文更受青睐。
3.
使Jython的lib目录对Servlet可用
:
有三种方法使Jython的lib目录中的模块对Servlet可用:
-
设置python.home属性
:可以设置环境变量
TOMCAT_OPTS
来设置
python.home
属性。建议在上下文的
WEB-INF
目录中创建一个名为
jylib
的目录,再在其中创建
Lib
目录,将所需模块放在该目录中,并将
jylib
目录指定为
python.home
。例如,在Windows上:
set TOMCAT_OPTS=-Dpython.home=%TOMCAT_HOME%\webapps\jython\WEB-INF\jylib
在*nix系统上:
export TOMCAT_OPTS=-Dpython.home=$TOMCAT_HOME/webapps/jython/WEB-INF/jylib
注意,某些Windows版本不允许环境字符串中包含等号,这种情况下需编辑
tomcat.bat
文件以包含
python.home
参数设置。
-
显式将模块目录添加到sys.path
:在Servlet顶部添加如下代码:
import sys
libpath = "c:/jakarta-tomcat_3.2.3/webapps/jython/WEB-INF/jylibs/Lib"
sys.path.append(libpath)
但这种方法通常需要显式的、依赖于机器的路径,限制了跨平台的可移植性。
-
冻结所需模块
:使用
jythonc --deep
选项编译所有所需模块,使
python.home
和Jython的lib目录不再重要。在Jython上下文的
classes
目录下,使用以下命令冻结
JythonServlet.py
文件:
jythonc -w . --deep JythonServlet.py
这样,JythonServlet和所有所需模块的类文件都位于上下文的
classes
目录中,即Servlet和模块安装在一个自包含的Web应用中。但要注意,用这种方式编译其他Jython Servlet会覆盖之前编译的模块,更新模块时需小心。编译后,生成的
.java
文件可删除。
4.4 测试Jython Servlet
编译
JythonServlet.py
文件、将jython.jar添加到类路径并使Jython的模块可访问后,在浏览器中访问
http://localhost:8080/jython/servlet/jythonServlet
,应看到消息“This is a Servlet of the Jython variety.”。若未看到该消息,可能出现以下三种错误:
- 若编译
JythonServlet
时类路径中没有
servlet.jar
文件,可能会看到
ClassCastException
。
- 若文件名和类名不同(即使只是大小写不同),也会看到
ClassCastException
。
- 若使用jythonc编译Servlet生成的类文件中有一个不在上下文的
classes
目录中,会看到
AttributeError
;若Jython的模块不可用,会看到
ImportError
。
5. 关于GenericServlet
JythonServlet.py
继承自
javax.Servlet.GenericServlet
,这是一个不特定于任何协议的Servlet类。
HttpServlet
在Web编程中更常见,因为它特定于HTTP协议,但
GenericServlet
是
HttpServlet
的超类,了解其方法很重要。以下是
GenericServlet
的一些方法及其在Jython子类中的使用示例:
| Java签名 | Jython子类中的用法 |
| — | — |
|
重写的方法
| |
|
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"
|
|
使用self.方法语法的方法
| |
|
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()
|
下面通过一个点击计数器Servlet示例来进一步了解这些方法。
5.1 点击计数器Servlet
以下是一个点击计数器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()
将
HitCounter.py
文件放在上下文的
classes
目录(
%TOMCAT_HOME%\webapps\jython\WEB-INF\classes
)中,使用以下命令编译:
jythonc -w . --deep HitCounter.py
在浏览器中访问
http://localhost:8080/jython/servlet/HitCounter
,第一次访问时应看到消息“Total Hits: 0”,每次后续点击都会增加计数。关闭并重启Tomcat后,点击计数应继续上次的数字。
5.2 三个重要方法
init
、
service
和
destroy
这三个方法对Servlet至关重要,每个方法对应Servlet生命周期的一个阶段:
-
init(ServletConfig)方法
:在Servlet启动时调用,且仅调用一次,适用于耗时的任务,如建立数据库连接、编译正则表达式或加载辅助文件。在上述示例中,该方法用于建立存储点击信息的文件,并将计数器设置为该文件中最后存储的整数,确保除非Servlet重启且
counter.txt
文件缺失,否则点击计数器不会重置为0。Servlet API 2.2版本有两个
init
方法:一个无参数版本和一个接受
ServletConfig
类实例的版本。在Jython中重写Java方法时,会重写所有同名方法,因此示例中通过为
cfg
变量指定默认参数
None
,然后测试
None
来决定是否在调用超类的
init
方法时使用该变量。
-
service(ServletRequest, ServletResponse)方法
:每次接收到客户端请求时调用,Servlet必须定义该方法,因为它在
GenericServlet
中是抽象的。该方法的参数是
ServletRequest
和
ServletResponse
对象,
ServletRequest
包含客户端发送到服务器的请求信息,
ServletResponse
用于将响应流以适当的mime编码格式发送回客户端。
-
destroy()方法
:在关闭或卸载Servlet时调用,这里放置清理代码,如关闭数据库连接、刷新和关闭文件等。在上述示例中,该方法将点击计数器的值写入文件,以防止Servlet卸载时信息丢失,但这不是最佳的持久化方法,因为只有在服务器正常关闭时点击计数才会存储。
6. HttpServlet
GenericServlet
适用于任何聊天类型的协议,但Web开发主要涉及HTTP协议,因此最好扩展
javax.Servlet.http.HttpServlet
。
HttpServlet
是
GenericServlet
的子类,因此扩展
HttpServlet
时可以使用
GenericServlet
的
init
、
service
和
destroy
方法。
6.1 HttpServlet的方法
HttpServlet
为每个HTTP方法定义了一个方法,如
doGet
、
doPost
、
doPut
、
doOptions
、
doDelete
和
doTrace
。当Tomcat接收到GET类型的客户端请求时,会调用请求的Servlet的
doGet()
方法进行响应。此外,
HttpServlet
有一个特定于HTTP的
service
方法,与
GenericServlet
的版本相比,其参数是特定于HTTP的请求和响应对象(
javax.servlet.http.HttpServletRequest
和
javax.servlet.http.HttpServletResponse
)。
以下是
HttpServlet
类的方法及其在Jython中的使用示例:
| Java签名 | Jython子类中的用法 |
| — | — |
|
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):
|
|
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):
|
接受特定于HTTP的请求和响应对象的
service
方法会将请求重新分发到适当的
do*
方法(如果未被重写),接受通用Servlet请求和响应对象的
service
方法会将请求重新分发到特定于HTTP的
service
方法,这种重新分发在实现Servlet映射时很有价值。
6.2 HttpServlet示例
以下是一个扩展
javax.servlet.http.HttpServlet
的Servlet示例:
#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
将
get_post.py
文件放在
$TOMCAT_HOME/webapps/jython/WEB-INF/classes
目录下,使用以下命令编译:
jythonc -w . --deep get_post.py
在浏览器中访问
http://localhost:8080/jython/servlet/get_post
,应看到一个类似于示例中描述的浏览器窗口。在这个示例中,GET操作的参数为空,但可以通过在URL末尾添加参数(如
http://localhost:8080/servlet/get_post?variable1=1&variable2=2
)来测试
doGet
方法中的其他参数。由于页面底部的提交按钮是一个POST表单,点击该按钮将执行同一个Servlet的
doPost
方法,结果应与示例中的描述相符。
6.3 HttpServletRequest和HttpServletResponse
与客户端连接的通信通过
HttpServletRequest
和
HttpServletResponse
对象进行,它们是从客户端接收和发送到客户端的实际请求流的抽象,每个对象都添加了更高级的、特定于HTTP的方法,以方便处理请求和响应。
以下是
HttpServletRequest
对象的一些方法及其对应的bean属性名称:
| 方法和属性 | 描述 |
| — | — |
|
getAuthType()
/
AuthType
| 返回描述认证类型名称的字符串(PyString),若用户未认证,值为None |
|
getContextPath()
/
contextPath
| 返回描述请求的上下文路径信息的字符串(PyString) |
|
getCookies()
/
cookies()
| 返回客户端请求发送的所有cookie,作为
javax.servlet.http.Cookie
对象的数组 |
|
getDateHeader(name)
| 以长类型检索指定头的值 |
|
getHeader(name)
| 以字符串(PyString)形式返回指定头的值 |
|
getHeaderNames()
/
headerNames
| 返回请求中包含的所有头名称的枚举 |
|
getHeaders(name)
| 返回指定头名称的所有值的枚举 |
|
getIntHeader(name)
| 以Java int类型检索指定头的值,Jython将其转换为PyInteger |
|
getMethod()
/
method
| 返回请求类型的字符串 |
|
getPathInfo()
/
pathInfo
| 客户端发送的所有额外路径信息 |
|
getPathTranslated()
/
pathTranslated
| 返回从客户端请求的额外路径信息派生的实际路径 |
|
getQueryString()
/
queryString
| 返回客户端请求的查询字符串(路径后的字符串) |
|
getRemoteUser()
/
remoteUser
| 返回客户端的登录名,若客户端未认证,值为None |
|
getRequestedSessionId()
/
requestedSessionId
| 返回客户端的会话ID |
|
getRequestURI()
/
requestURI
| 返回协议名称和查询字符串之间的部分 |
|
getServletPath()
/
servletPath
| 返回指定当前Servlet的URL部分 |
|
getSession()
/
session
| 返回当前会话,若需要则创建一个会话,会话是
javax.servlet.http.HttpSession
的实例 |
|
getSession(create)
| 若存在当前会话则返回,若不存在且
create
值为
true
则创建一个新会话 |
|
getUserPrincipal()
/
userPrincipal
| 返回包含当前认证信息的
java.security.Principal
对象 |
|
isRequestedSessionIdFromCookie()
| 根据当前会话ID是否来自cookie返回1或0 |
|
isRequestedSessionIdFromURL()
| 根据当前会话ID是否来自请求的URL字符串返回1或0 |
|
isRequestedSessionIdValid()
| 根据请求的会话ID是否仍然有效返回1或0 |
|
isUserInRole(role)
| 指示用户是否在指定角色中,返回1或0 |
HttpServletResponse
对象用于将mime编码的流发送回客户端,它定义了一些在通用
ServletResponse
对象中不存在的特定于HTTP的方法。以下是
HttpServletResponse
对象的一些方法及其描述:
| 方法和属性 | 描述 |
| — | — |
|
addCookie(cookie)
| 向响应中添加一个cookie |
|
addDateHeader(headerName, date)
| 添加一个带有日期(长类型)值的头名称 |
|
addHeader(headerName, value)
| 添加一个头名称和值 |
|
addIntHeader(headerName, value)
| 添加一个带有整数值的头名称 |
|
containsHeader(headerName)
| 根据指定的头是否存在返回1或0 |
|
encodeRedirectUrl(url)
| 为
sendRedirect
方法编码URL(2.1及更高版本使用
encodeRedirectURL
) |
|
encodeRedirectURL(url)
| 为
sendRedirect
方法编码URL |
|
encodeURL(url)
| 通过包含会话ID对URL进行编码 |
|
sendError(sc)
| 使用状态码发送错误 |
|
sendError(sc, msg)
| 使用指定的状态码和消息发送错误 |
|
sendRedirect(location)
| 发送临时重定向到指定位置 |
|
setDateHeader(headerName, date)
| 将头名称设置为指定的日期(长类型)值 |
|
setHeader(headerName, value)
| 将头名称设置为指定的值 |
|
setIntHeader(headerName, value)
| 将头名称设置为指定的整数值 |
|
setStatus(statusCode) / status
| 设置响应状态码 |
HttpServletResponse
类还包含与标准HTTP响应代码对应的字段,可与
sendError(int)
和
setStatus(int)
一起使用,以下是一些常见的状态码及其含义:
| 错误代码 | 状态 |
| — | — |
| 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 |
7. PyServlet
对于Jython程序员来说,
HttpServlet
编程的一个很大优势是Jython发行版附带了
org.python.util.PyServlet
。这个Servlet可以加载、执行和缓存Jython文件,这样就可以编写和查看Jython Servlet而无需中间的编译步骤。这通过Servlet映射实现,Servlet映射是特定Servlet(这里是
PyServlet
)与特定URL模式(如
*.py
)之间的关联。通过适当的
web.xml
文件,Jython的
PyServlet
类被映射到所有
*.py
文件,当请求
*.py
文件时,
PyServlet
类会根据需要加载、缓存和调用
*.py
文件中的方法以进行响应。
7.1 PyServlet的限制
PyServlet
的设计对其服务的Jython文件有一些限制。Jython文件必须包含一个继承自
javax.servlet.http.HttpServlet
的类,该类的名称必须与文件名称(不带
.py
扩展名)匹配。此外,除了继承自
HttpServlet
的那个类之外,使用模块全局标识符是不安全的。
为了避免命名混淆,这里将实现Servlet的Jython模块称为jylets。
7.2 安装PyServlet
安装
PyServlet
只需定义一个Servlet映射并确保
jython.jar
文件在上下文的
lib
目录中。Servlet映射在上下文的部署描述符文件
web.xml
中定义,Jython上下文的
web.xml
文件位于
$TOMCAT_HOME/webapps/jython/WEB-INF/web.xml
。在Tomcat中,若上下文的
web.xml
未明确包含某些设置,则使用
$TOMCAT_HOME/conf/web.xml
中的默认
web.xml
文件。因此,上下文可能还没有
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>
</servlet>
<servlet-mapping>
<servlet-name>PyServlet</servlet-name>
<url-pattern>*.py</url-pattern>
</servlet-mapping>
</web-app>
这个示例定义了
PyServlet
的名称和类,类是唯一标识Servlet类的完整包类层次结构,实际类位于
jython.jar
文件中,该文件应在上下文的
lib
目录中。
PyServlet
的Servlet定义可以选择包含初始化参数(
init-params
),
PyServlet
在初始化Jython时使用这些参数,因此可以在这里设置Jython属性,如
python.home
、
python.path
、
python.respectJavaAccessibility
等。以下是一个包含
python.home
和
python.path
值作为
init-params
的
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>
定义Jython资源位置的属性(如
python.home
和
python.path
)需要特别注意,它们会影响上下文是否自包含以及跨平台的可移植性。若
python.home
属性指向上下文之外,或
python.path
属性包含上下文之外的目录,则上下文不再自包含,且属性值必须是特定于平台的路径。幸运的是,
PyServlet
的
python.home
属性有一个默认值,可创建自包含且与平台无关的上下文。
PyServlet
的默认
python.home
值是上下文的
lib
目录,这使得Jython上下文的默认
python.home
值根据平台不同可能是
%TOMCAT_HOME\webapps\jython\WEB-INF\lib
或
$TOMCAT_HOME/webapps/jython/WEB-INF/lib
。此外,Jython的
lib
目录自动变为
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib
或
$TOMCAT_HOME/webapps/jython/WEB-INF/lib/Lib
,这将所有Jython资源保留在上下文中,使其自包含,并且
PyServlet
以与平台无关的方式添加默认
python.home
路径,使使用默认值的上下文具有平台独立性。
7.3 测试PyServlet
可以通过启动Tomcat并在浏览器中查看一个简单的jylet来确认Servlet映射是否正常工作。以下是一个简单的测试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."
将这个测试文件保存为
%TOMCAT_HOME%\webapps\jython\ServletMappingTest.py
,然后在浏览器中访问
http://localhost:8080/jython/ServletMappingTest.py
,若看到问候消息,则
PyServlet
映射正确。为完成安装,创建Jython的
lib
目录
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib
,将在jylets中使用的任何Jython模块放在该目录中,安装即完成。
8. Cookies
Cookies允许在客户端机器上存储信息,这些信息将包含在后续请求中。在Jython中创建和操作Cookies可使用
javax.Servlet.http.Cookie
类。创建新的Cookie需要使用名称和值作为参数实例化
javax.servlet.http.Cookie
。例如,若使用Cookies跟踪图书购买情况,可以这样设置作者和标题:
from javax.servlet import http
name = "book1"
value = "Lewis Carroll, Alice In Wonderland"
MyNewCookie = http.Cookie(name, value)
要将这个Cookie发送给客户端,需要使用
HttpServletResponse
的
addCookie(cookie)
方法:
res.addCookie(MyNewCookie)
添加Cookies应在通过响应流发送其他内容之前进行。
每个Cookie实例可以使用
get
和
set
方法或Jython的自动bean属性来设置以下属性:
- comment
- domain
- maxAge
- name
- path
- secure
- value
- version
以下是一个使用Cookie对象的自动bean属性创建和读取Web表单中定义的Cookies的示例:
# 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
<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>"""
测试这个jylet时,先确保浏览器允许使用Cookies,将
cookies.py
文件放在
%TOMCAT_HOME%\webapps\jython
目录中,在浏览器中访问
http://localhost:8080/jython/cookies.py
,首次访问时应只看到标题和表单。在表单中输入信息(如名称为“Jython Cookie”,值为“My first Jython cookie”),点击提交按钮(
maxAge
可选),应看到添加Cookie成功的确认信息。返回
doGet
方法,查看Cookie列表以确认Cookie是否真正添加。
9. Sessions
Cookies是与客户端创建会话的最常见方式,会话用于通过一系列请求跟踪客户端信息。虽然前面的Cookie示例可以用于存储会话ID,但Java的
HttpSession
类使会话跟踪更加容易。
要创建会话,使用
HttpRequest
的
getSession()
方法,该方法返回一个
HttpSession
实例。
HttpSession
是会话管理子系统更复杂行为的简单接口,在Jython中使用
HttpSession
对象与Java的区别仅在于语法和对象的
get*
方法的自动bean属性。
以下是一个会话管理示例:
# 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>
"""
将
session.py
文件保存到
%TOMCAT_HOME%\webapps\jython
目录中,在浏览器中访问
http://localhost:8080/jython/session.py
,使用网页表单向会话中添加一些变量以确认其正常工作,甚至可以尝试禁用Cookies,查看URL重写如何工作。
10. 数据库和Servlet
在jylet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用没有区别,但管理数据库连接和其他资源是一个主要问题,因为许多Web应用包含大量使用数据库内容的jylets。在每个jylet中创建数据库连接会迅速消耗数据库资源,而为每个请求创建和关闭数据库连接的开销又不可接受。
管理数据库资源有多种选择,主要有两种方法:每个jylet使用一个连接或连接池。每个jylet使用一个连接是一种流行但资源密集的方法,该方法在jylet的
init
方法中建立数据库连接,仅在jylet卸载时关闭该连接,这样在响应客户端请求时消除了连接开销,但只有在所需连接数量在资源限制范围内时才合理,大多数情况需要更谨慎的资源管理。
以下是一个在
init
方法中获取数据库连接和游标对象,并在
destroy
方法中关闭它们的jylet示例:
# 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()
这个示例使用了
zxJDBC
包和MySQL数据库,因此必须将MySQL和
zxJDBC
所需的类放在上下文的
lib
目录中,如
mm_mysql-2_0_4-bin.jar
文件和
zxJDBC.jar
文件应放在
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib
目录中,添加jar文件后重启Tomcat以确保其检测到新的jar文件。
示例假设存在一个名为
products
的数据库,其中包含
products
表,该表至少有
code
、
name
、
description
和
price
字段。创建数据库和表的SQL语句如下:
create database products
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;
创建数据库和表,为每个字段创建一些任意值,将
DBDisplay.py
文件放在上下文的根目录中,在浏览器中访问
http://localhost:8080/jython/DBDisplay.py
,应能看到数据库数据。
若Web应用开始使用过多连接,可考虑使用连接池。连接池允许谨慎管理资源并消除连接开销,它维护一定数量的活动数据库连接,jylets在回复客户端请求时借用这些连接,完成后归还到池中,这样可以创建可预测的、静态数量的连接,也可以使用语句池,使语句具有相同的优势。一个流行、免费、经过测试且文档完善的连接池工具是PoolMan(http://www.codestudio.com/),其他Java连接池包也存在,且都应能与Jython无缝配合使用。
11. JSP
Java Server Pages(JSP)是Sun倡导的作为Servlet补充的模板系统。JSP文件包含网页的标记代码和文本,同时包含指定动态内容的特殊标签。目前,Tomcat仅在JSP中实现Java语言,那么如何在JSP中使用Jython呢?目前的答案是不能直接使用Jython,不能在代码标签(
<% code %>
)中使用Jython语言,但可以使用jythonc编译的类、嵌入PythonInterpreter或创建特定于Jython的自定义标签库。
11.1 jythonc编译的类和JSP
在JSP中使用jythonc编译的类需要创建一个与Java兼容的Jython类,该类的名称与模块名称相同,继承自Java类,并且为每个非从超类派生的方法包含
@sig
字符串。将jythonc生成的类文件放在上下文的
classes
目录中,JSP页面就可以像使用任何原生Java类一样使用这些类,当然,这也要求
jython.jar
文件放在上下文的
lib
目录中。
JSP文件可以通过两种方式使用jythonc编译的类:在脚本片段中使用或作为bean使用。若作为bean使用,Jython类必须符合bean约定,为每个读写属性包含
getProperty
和
setProperty
方法;而脚本片段可以使用任何类。无论作为bean还是在脚本片段中使用,都必须先将jythonc编译的类加载到适当的作用域中。导入非bean类使用页面导入指令:
<%@ page import="fully.qualified.path.to.class" %>
对于bean,使用
jsp:useBean
标签加载bean:
<jsp:useBean name="beanName"
class="fully.qualified path.to.class"
scope="scope(page or session)">
使用非bean类时,在脚本片段标签(
<% %>
)或表达式标签(
<%= %>
)中包含使用该类的Java代码。例如,若导入了假设的类
ProductListing
,可以在JSP页面中这样使用:
<%@ 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>
脚本片段通常不被推荐,因为它们会使JSP页面复杂化,首选的实现方式是使用jythonc编译的bean以及JSP的
useBean
、
setProperty
和
getProperty
标签。以下是一个简单的用Jython编写的bean示例:
# 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
将
nameHandler.py
文件放在
%TOMCAT_HOME%\webapps\jython\WEB-INF\classes
目录中,在该目录下使用以下命令编译:
jythonc -w . Namehandler.py
以下是一个使用该bean的简单JSP页面示例:
<!--file: name.jsp -->
<%@ page contentType="text/html"
### Jython在服务器端Web编程中的应用
##### 11.2 嵌入PythonInterpreter在JSP中
若想在JSP脚本片段中使用Jython代码,可以通过`PythonInterpreter`实例间接实现。这需要使用导入指令导入`org.python.util.PythonInterpreter`。以下是一个简单的JSP页面示例,展示了如何使用`PythonInterpreter`对象在JSP页面中包含Jython代码:
```jsp
<!--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.println('Hello from JSP and the Jython interpreter.')"); %>
</body>
</html>
要使用
interp.jsp
文件,需确保
jython.jar
文件在上下文的
lib
目录中,并将
interp.jsp
文件放在上下文的根目录(
%TOMCAT_HOME%\webapps\jython
)中。在浏览器中访问
http://localhost:8080/jython/interp.jsp
,应能看到Jython解释器输出的简单消息。
需要注意的是,很多人认为脚本片段不是好的做法,在脚本片段中从Java使用Jython会增加复杂度,显然不太可取。有更好的方法来创建动态内容,如使用bean类和标签库。
11.3 Jython标签库
标签库是可以在JSP页面中使用的自定义标签库。可以使用
jythonc
将Jython标签库模块编译成Java类来创建Jython标签库。Jython标签库模块必须符合一定的限制才能像Java类一样透明地工作。模块必须包含一个与模块名称相同(不带
.py
扩展名)的类,该类必须继承自
javax.servlet.jsp.tagext.Tag
接口或实现该接口的Java类,并且编译后的类文件必须能够访问
org.python.*
包和类以及Jython库模块(如果标签库导入了这些模块)。
为了使标签库能够访问所有必需的类和库,可以使用
jythonc
的
--all
选项进行编译,就像之前编译Servlet那样。但这样做可能会导致重复编译Jython核心文件,造成资源浪费。更好的方法是将
jython.jar
文件放在上下文的
lib
目录中,在上下文中为Jython的模块建立一个
lib
目录(推荐为
{context}\WEB-INF\lib\Lib
),然后编译Jython标签库模块而不包含依赖项。
以下是一个简单的Jython标签库模块示例:
# 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
在这个示例中,需要特别注意保存页面上下文和父标签信息的实例变量(
self.context
和
self.paren
)。这些变量不能被标识为
self.pageContext
和
self.parent
,或者所有对这些变量的访问都必须通过实例的
__dict__
进行。因为
Tag
接口要求实现
setPageContext()
、
setParent()
和
getParent()
方法,Jython会为这些方法的相关属性名称创建自动bean属性,这可能会导致循环引用和
StackOverflowExceptions
。
安装标签库所需的类时,首先要确保
jython.jar
文件在上下文的
lib
目录中,然后使用以下命令编译
JythonTag.py
文件:
jythonc -w %TOMCAT_HOME%\webapps\jython\WEB-INF\classes JythonTag.py
编译后,上下文的
classes
目录中会创建
JythonTag.class
和
JythonTag$_PyInner.class
文件。但仅这些类文件还不足以使用标签库,在JSP页面中使用标签之前,还必须创建一个标签库描述文件。以下是一个适合描述上述标签的标签库描述文件示例:
<?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>
将上述标签库描述信息放在
%TOMCAT_HOME%\webapps\jython\WEB-INF\jython-taglibs.tld
文件中,JSP页面将使用该文件来识别页面中使用的标签。
标签库描述符标识了标签库的特征,如版本号、适用的JSP版本等,还指定了一个用于引用该标签库的名称(
<shortname>
)。其余元素定义了在上述示例中创建的特定标签库。
<tag>
元素标识了完全限定的类名,并为该类分配了一个名称。
使用标签库时,JSP页面必须包含一个指令,指定标签库描述符的路径并为该库分配一个简单的名称。所有后续对该库中标签的引用都将以该指令中分配的名称开头。以下是一个JSP指令示例,用于引用上述创建的标签库:
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
由于
.tld
文件为示例标签指定了名称
message
,声明该标签库后,可以使用以下方式使用
message
标签:
<jython:message/>
以下是一个使用上述标签和标签库描述符的JSP文件示例:
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
<html>
<body>
<jython:message />
</body>
</html>
将
test.jsp
文件保存为
%TOMCAT_HOME%\webapps\jython\test.jsp
,在浏览器中访问
http://localhost:8080/jython/test.jsp
,应能看到自定义标签的消息。
使用编译后的Jython模块实现标签库时,会再次涉及Jython的主目录和
lib
目录的问题。
jythonc
编译的标签库不知道
python.home
在哪里或在哪里找到库模块,除非在
PySystemState
中建立了这些信息。可以在
TOMCAT_OPTS
环境变量中设置
python.home
属性,也可以利用
PyServlet
类来建立这些系统状态信息。如果上下文加载了
PyServlet
类,那么
python.home
和
sys.path
信息可能已经为需要的标签库设置正确。在默认的
PyServlet
设置下,
python.home
是
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib
,因此Jython的
lib
目录是
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib
。加载
PyServlet
后,标签库可以从同一个
lib
目录加载Jython模块。
11.4 BSF
IBM的Bean Scripting Framework(BSF)是一个实现各种脚本语言的Java工具,Jython是目前BSF支持的语言之一。Apache Jakarta项目包含一个名为
taglibs
的子项目,它是一个广泛的Java标签库集合,其中有趣的是BSF标签库。BSF标签库与BSF的jar文件结合使用,可以让你快速将Jython脚本片段和表达式直接插入JSP页面。
目前,BSF发行版需要进行一些调整才能与Jython配合使用。由于BSF发行版最终会包含这些小更改,这里不再详细说明,但这意味着你需要确认你的BSF版本包含这些更改。为了方便起见,相关网站会提供正确的
bsf.jar
文件下载链接。下载
bsf.jar
文件后,将其放在上下文的
lib
目录(
%TOMCAT_HOME%\webapps\jython\WEB-INF\lib
)中。相关网站还会提供BSF标签库和
bsf.tld
文件的下载链接,将包含BSF标签库的jar文件放在上下文的
lib
目录中,将
bsf.tld
文件放在上下文的
WEB-INF
目录(
%TOMCAT_HOME%\webapps\jython\WEB-INF\bsf.tld
)中。
安装这些文件后,就可以在JSP文件中使用Jython脚本片段。首先,需要包含一个指令来识别BSF标签库:
<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %>
然后,可以使用
bsf:scriptlet
标签,在标签体中包含Jython代码,示例如下:
<bsf:scriptlet language="jython">
import random
print >> out, random.randint(1, 100)
</bsf:scriptlet>
脚本片段在解释器中自动设置了一些JSP对象,以下是这些对象及其Java类名的列表:
| 对象 | Java类名 |
| — | — |
|
request
|
javax.servlet.http.HttpServletRequest
|
|
response
|
javax.servlet.http.HttpServletResponse
|
|
pageContext
|
javax.servlet.jsp.PageContext
|
|
application
|
javax.servlet.ServletContext
|
|
out
|
javax.servlet.jsp.JspWriter
|
|
config
|
javax.servlet.ServletConfig
|
|
page
|
java.lang.Object
|
|
exception
|
java.lang.Exception
|
|
session
|
javax.servlet.http.HttpSession
|
大多数对象在Servlet中很常见,BSF脚本片段标签允许在JSP页面中使用这些对象。以下是一个使用
bsf:scriptlet
标签在JSP文件中创建时间戳的示例:
<%@ 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>\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>
将上述示例保存为
%TOMCAT_HOME%\webapps\jython\scriptlets.jsp
文件,在浏览器中访问
http://localhost:8080/jython/scriptlets.jsp
,应能看到所有客户端信息以及访问时间。
总结
综上所述,Jython在服务器端Web编程中具有广泛的应用场景和多种实现方式。从简单的Servlet编写到复杂的JSP页面动态内容生成,从Cookie和会话管理到数据库资源的有效利用,Jython都能发挥重要作用。通过合理选择和运用不同的技术手段,如使用
PyServlet
避免编译步骤、利用连接池管理数据库连接、创建自定义标签库等,可以提高开发效率、增强代码的灵活性和可维护性,同时充分利用Java生态系统的丰富资源。在实际开发中,开发者可以根据具体需求和项目特点,灵活组合这些技术,构建出高效、稳定的服务器端Web应用。
以下是一个简单的mermaid流程图,展示了Jython Servlet开发的基本流程:
graph LR
A[选择Servlet容器] --> B[定义Servlet类]
B --> C{选择编译方式}
C -->|jythonc编译| D[编译Jython代码]
C -->|PyServlet映射| E[配置web.xml]
D --> F[部署类文件]
E --> F
F --> G[启动Tomcat服务器]
G --> H[测试Servlet]
这个流程图概括了从选择Servlet容器开始,到最终测试Servlet的整个开发过程,帮助开发者更好地理解和掌握Jython Servlet开发的关键步骤。
超级会员免费看
50

被折叠的 条评论
为什么被折叠?



