Jython在服务器端Web编程中的应用
1. Jython在Web开发中的定位
Jython适用于Java适用的任何地方,尤其在需要快速开发和更高灵活性的Java项目中表现出色。服务器端Java Web编程主要通过Servlet和Java Server Pages(JSP)实现,因此Jython的主要实现方式也是Servlet和JSP。不过,Java的企业级包(j2ee)在Web应用开发中也很重要,如EJB、JNDI、JDBC等,Jython在这些技术中同样有效。但需要注意的是,Jython并不适合用于实现CGI脚本,因为JVM的启动时间会导致性能问题,而Servlet和JSP具有持久性,能在Web请求之间保持在内存中。
使用Jython进行Web编程有很多优势,高质量的Servlet容器随处可得且广泛部署。Java Servlet应用可以受益于Jython的灵活性和高级语言特性,同时还能利用Java和Jython的库。常见的Servlet容器有WebLogic、WebSphere、Tomcat、Jigsaw、Resin和Jetty等。
2. Jython Servlet容器
Jython可以与任何兼容的Java Servlet容器一起使用,以下是一些常用的Servlet容器:
-
Jakarta的Tomcat
:是Servlet和Java Server Pages规范的参考实现,本文使用的版本是3.2.3,支持2.2 Servlet和1.1 JSP规范。Tomcat 4.0实现了2.3 Servlet和1.2 JSP规范。所有Jython Servlet都在Tomcat 3.2.3上进行了测试,示例应适用于任何符合Servlet 2.2/JSP 1.1规范的容器。Tomcat发行版包含了本文所需的Servlet和JSP类,无需额外下载。
-
Apache JServ
:是为Apache创建的Servlet(版本2.0)引擎,通常被广泛部署。它是使用Jython Servlet的简单方法,如果当前开发已经使用了JServ,这可能是一个不错的选择。
-
Jigsaw
:是W3C的实验性Web服务器,虽然名为“实验性”,但实际上比这个标签所显示的更成熟。它是一个完全符合HTTP/1.1的Web服务器和缓存代理服务器,用Java编写,支持Servlet 2.2规范和Java Server Pages 1.1。
-
Jetty
:是一个紧凑高效的Java Web服务器,支持Servlet 2.2和JSP 1.1规范,支持HTTP 1.1,包括SSL支持,并能轻松与EJB服务器(如JBoss)集成。
这些工具的文档都很丰富,安装指南可以从它们各自的网站获取。
3. 定义简单的Servlet类
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>");
}
}
这个示例使用了
GenericServlet
类,
service
方法是抽象的,必须在子类中实现。
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>""")
与Java Servlet类似,它继承自
GenericServlet
并实现了
service
方法,但语法有所不同。
3.3 测试Java和Jython Servlet
3.3.1 安装Tomcat
安装Tomcat的步骤如下:
1. 从http://jakarta.apache.org 下载Tomcat,建议下载jakarta - tomcat - 3.2.3版本,根据平台选择zip或tar.gz文件。
2. 将存档文件解压到有足够权限的目录,设置
TOMCAT_HOME
环境变量。例如,在Windows上:
set TOMCAT_HOME=c:\jakarta-tomcat-3.2.3
在*nix上:
export TOMCAT_HOME=/usr/local/jakarta-tomcat-3.2.3
-
设置
JAVA_HOME环境变量,例如:
# on Windows
set JAVA_HOME=c:\jdk1.3.1
# bash (*nix) setup
export JAVA_HOME=/usr/java/jdk1.3.1
- 使用适合平台的启动脚本启动Tomcat:
# Windows
%TOMCAT_HOME%\bin\startup.bat
# bash (*unix)
$TOMCAT_HOME/bin/startup.sh
当看到
date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080
时,Tomcat正在运行并准备在端口8080上接受连接。
5. 停止Tomcat时,使用相应的关闭脚本:
# Windows
%TOMCAT_HOME%\bin\shutdown.bat
# bash (*nix)
$TOMCAT_HOME/bin/shutdown.sh
3.3.2 安装Java Servlet
将
JavaServlet.java
文件放在
%TOMCAT_HOME%\webapps\jython\WEB - INF\classes
目录中,从该目录下使用以下命令编译:
javac -classpath %TOMCAT_HOME%\lib\servlet.jar JavaServlet.java
编译完成后,启动Tomcat,然后在浏览器中访问http://localhost:8080/jython/servlet/JavaServlet,应该能看到消息“This is a Java Servlet.”
3.3.3 安装Jython Servlet
使用Jython Servlet有两种方式,本文使用
jythonc
编译的方式,步骤如下:
1.
使用jythonc编译Jython Servlet模块
:确保
CLASSPATH
包含
Servlet.jar
,在
%TOMCAT_HOME%\jython\WEB - INF\classes
目录下使用以下命令编译:
jythonc -w . JythonServlet.py
编译时要注意查看是否有
Creating .java files: JythonServlet module JythonServlet extends javax.servlet.GenericServlet
的提示,若没有则需要检查
CLASSPATH
和设置选项并重新编译。
2.
将jython.jar添加到类路径
:有三种方法:
- 将
jython.jar
添加到上下文的
lib
目录,这是首选方法。
- 将
jython.jar
添加到Tomcat的
lib
目录,不推荐这种方法。
- 将
jython.jar
留在Jython的安装目录,但在运行Tomcat之前将其添加到类路径。
3.
使Jython的lib目录对Servlet可用
:有三种方法:
- 设置
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
- 冻结所需的模块,使用`jythonc --deep`选项编译,例如:
jythonc -w . --deep JythonServlet.py
- 让每个Servlet显式地将模块位置附加到`sys.path`,例如:
import sys
libpath = "c:/jakarta-tomcat_3.2.3/webapps/jython/WEB-INF/jylibs/Lib"
sys.path.append(libpath)
编译完成后,在浏览器中访问http://localhost:8080/jython/servlet/jythonServlet,应该能看到消息“This is a Servlet of the Jython variety.”。如果看不到,可能会出现
ClassCastException
、
AttributeError
或
ImportError
等错误。
4. 关于GenericServlet
GenericServlet
是一个不特定于任何协议的Servlet类,
HttpServlet
更常用于Web编程,因为它特定于HTTP协议,但
GenericServlet
是
HttpServlet
的超类,了解其方法很重要。以下是
GenericServlet
的一些方法及其在Jython子类中的使用示例:
| Java Signature | Usage in Jython Subclass |
| — | — |
|
public void init(ServletConfig config) throws ServletException;
|
def init(self, config):
|
|
public void destroy();
|
def destroy(self):
|
|
public abstract void service(ServletRequest request, ServletResponse response) throws ServletException, IOException;
|
def service(self, request, response):
|
|
public String getServletInfo();
|
def getServletInfo(self): return "Info string"
|
|
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
|
4.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)
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:
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
目录中,使用
jythonc -w . --deep HitCounter.py
编译,然后在浏览器中访问http://localhost:8080/jython/servlet/HitCounter,第一次访问时应该看到“Total Hits: 0”,每次后续访问计数会增加。
4.2 各方法的作用
- init(ServletConfig)方法 :在Servlet启动时调用,仅调用一次,可用于执行耗时任务,如设置数据库连接、编译正则表达式或加载辅助文件。在计数器Servlet中,该方法用于建立存储命中信息的文件并设置计数器。
- service(ServletRequest, ServletResponse)方法 :响应每个客户端请求时调用,Servlet必须定义此方法。
-
destroy()方法
:在关闭或卸载Servlet时调用,用于清理代码,如关闭数据库连接、刷新和关闭文件等。但在计数器Servlet中,这种持久化方式不是最好的,因为如果服务器不正常关闭,
destroy方法可能不会被调用。
5. HttpServlet
HttpServlet
是
GenericServlet
的子类,特定于HTTP协议。它为每个HTTP方法定义了方法,如
doGet
、
doPost
等。当Tomcat接收到GET类型的客户端请求时,会调用请求的Servlet的
doGet()
方法进行响应。
5.1 HttpServlet方法
| Java Signature | Usage in Jython Subclass |
|---|---|
doDelete(HttpServletRequest req, HttpServletResponse resp)
|
def doDelete(self, req, res):
|
doGet(HttpServletRequest req, HttpServletResponse resp)
|
def doGet(self, req, res):
|
doHead(HttpServletRequest req, HttpServletResponse resp)
|
def doHead(self, req, res):
|
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):
|
5.2 HttpServlet示例
以下是一个使用
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
将
get_post.py
放在
$TOMCAT_HOME/webapps/jython/WEB - INF/classes
目录中,使用
jythonc -w . --deep get_post.py
编译,然后在浏览器中访问http://localhost:8080/jython/servlet/get_post,点击提交按钮可以测试
doPost
方法。
5.3 HttpServletRequest和HttpServletResponse
与客户端连接的通信通过
HttpServletRequest
和
HttpServletResponse
对象进行,它们为处理请求和响应提供了更高级的HTTP特定方法。
5.3.1 HttpServletRequest方法和属性
| Method and Property | Description |
|---|---|
getAuthType()
AuthType
|
返回描述认证类型名称的字符串,若用户未认证则为
None
。
|
getContextPath()
contextPath
| 返回标识请求上下文的路径信息字符串。 |
getCookies()
cookies()
|
返回客户端请求中发送的所有cookie,作为
javax.servlet.http.Cookie
对象的数组。
|
getDateHeader(name)
| 以长类型检索指定头的值。 |
getHeader(name)
| 以字符串形式返回指定头的值。 |
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。 |
5.3.2 HttpServletResponse方法和属性
| Method and Property | Description |
|---|---|
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
| 设置响应状态码。 |
5.3.3 HttpServletResponse状态码
| Error Code | Status |
|---|---|
| 100 |
SC_CONTINUE
|
| 101 |
SC_SWITCHING_PROTOCOLS
|
| 200 |
SC_OK
|
| 201 |
SC_CONTINUE
|
| 202 |
SC_ACCEPTED
|
| 203 |
SC_NON_AUTHORITATIVE_INFORMATION
|
| 204 |
SC_NO_CONTENT
|
| 205 |
SC_RESET_CONTENT
|
| 206 |
SC_PARTIAL_CONTENT
|
| 300 |
SC_MULTIPLE_CHOICES
|
| 301 |
SC_MOVED_PERMANENTLY
|
| 302 |
SC_MOVED_TEMPORARILY
|
| 303 |
SC_SEE_OTHER
|
| 304 |
SC_NOT_MODIFIED
|
| 305 |
SC_USE_PROXY
|
| 400 |
SC_BAD_REQUEST
|
| 401 |
SC_UNAUTHORIZED
|
| 402 |
SC_PAYMENT_REQUIRED
|
| 403 |
SC_FORBIDDEN
|
| 404 |
SC_NOT_FOUND
|
| 405 |
SC_METHOD_NOT_ALLOWED
|
| 406 |
SC_NOT_ACCEPTABLE
|
| 407 |
SC_PROXY_AUTHENTICATION_REQUIRED
|
| 408 |
SC_REQUEST_TIMEOUT
|
| 409 |
SC_CONFLICT
|
| 410 |
SC_GONE
|
| 411 |
SC_LENGTH_REQUIRED
|
| 412 |
SC_PRECONDITION_FAILED
|
| 413 |
SC_REQUEST_ENTITY_TOO_LARGE
|
| 414 |
SC_REQUEST_URI_TOO_LONG
|
| 415 |
SC_UNSUPPORTED_MEDIA_TYPE
|
| 500 |
SC_INTERNAL_SERVER_ERROR
|
| 501 |
SC_NOT_IMPLEMENTED
|
| 502 |
SC_BAD_GATEWAY
|
| 503 |
SC_SERVICE_UNAVAILABLE
|
| 504 |
SC_GATEWAY_TIMEOUT
|
| 505 |
SC_HTTP_VERSION_NOT_SUPPORTED
|
6. PyServlet
Jython发行版包含
org.python.util.PyServlet
,它可以加载、执行和缓存Jython文件,无需中间编译步骤。使用Servlet映射将
PyServlet
与特定的URL模式(如
*.py
)关联。
6.1 安装PyServlet
安装
PyServlet
只需定义一个Servlet映射并确保
jython.jar
文件在上下文的
lib
目录中。在上下文的
web.xml
文件中定义Servlet映射,示例如下:
<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
的
python.home
属性默认值是上下文的
lib
目录,这使得上下文既自包含又跨平台中立。
6.2 测试PyServlet
以下是一个简单的测试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
映射正确。最后创建
%TOMCAT_HOME%\webapps\jython\WEB - INF\lib\Lib
目录,安装完成。
7. Cookies
使用
javax.Servlet.http.Cookie
类可以在Jython中创建和操作cookie。示例如下:
from javax.servlet import http
name = "book1"
value = "Lewis Carroll, Alice In Wonderland"
MyNewCookie = http.Cookie(name, value)
res.addCookie(MyNewCookie)
添加cookie应在通过响应流发送其他内容之前进行。每个cookie实例可以使用
get
和
set
方法或Jython的自动bean属性来设置以下属性:
comment
、
domain
、
maxAge
、
name
、
path
、
secure
、
value
、
version
。
以下是一个使用cookie的示例:
# 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>"""
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>"""
将该文件放在
%TOMCAT_HOME%\webapps\jython
目录中,在浏览器中访问http://localhost:8080/jython/cookies.py,按照提示操作可以测试cookie的创建和读取。
8. Sessions
使用
HttpRequest
的
getSession()
方法可以创建会话,返回一个
HttpSession
实例。以下是一个会话管理的示例:
# 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>
"""
将该文件保存为
%TOMCAT_HOME%\webapps\jython\session.py
,在浏览器中访问http://localhost:8080/jython/session.py,使用表单添加变量可以测试会话管理。如果客户端禁用了cookie,会话对象仍然可以通过
encodeUrl
方法重写URL来工作。
9. 数据库和Servlet
在jylet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用没有区别,但管理数据库资源是一个主要问题。有两种主要的方法:每个jylet一个连接或连接池。
9.1 每个jylet一个连接
以下是一个在
init
方法中获取数据库连接和游标对象,并在
destroy
方法中关闭它们的示例:
# file: DBDisplay.py
from javax.servlet import http
from com.ziclix.python.sql import zxJDBC
class DBDisplay(http.HttpServlet):
def init(self, cnfg):
url = "jdbc:mysql://192.168.1.77/products"
usr = "productsUser"
passwd = "secret"
driver = "org.gjt.mm.mysql.Driver"
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()
要创建所需的数据库和表,可以使用以下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可以查看数据库数据。
9.2 连接池
如果Web应用开始使用过多的连接,可以考虑使用连接池。连接池可以保持一定数量的活动数据库连接,jylet在响应客户端请求时借用连接并在完成后返回。流行的连接池工具如PoolMan(http://www.codestudio.com/)等都可以与Jython无缝配合使用。
10. JSP
目前Tomcat在JSP中只支持Java语言,要在JSP中使用Jython,可以使用
jythonc
编译的类、嵌入
PythonInterpreter
或创建Jython特定的自定义标签库。
10.1 jythonc编译的类和JSP
使用
jythonc
编译的类与JSP配合使用,需要创建一个与Java兼容的Jython类,将生成的类文件放在上下文的
classes
目录中,并确保
jython.jar
文件在上下文的
lib
目录中。JSP文件可以以两种方式使用
jythonc
编译的类:作为脚本片段或作为bean。
以下是一个简单的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
将该文件放在
%TOMCAT_HOME%\webapps\jython\WEB - INF\classes
目录中,使用
jythonc -w . Namehandler.py
编译。以下是一个使用该bean的JSP页面示例:
<!--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>
将
name.jsp
文件放在上下文的根目录中,在浏览器中访问http://localhost:8080/jython/name.jsp,可以看到默认名称
Fred
和修改后的名称
Robert
。
10.2 嵌入PythonInterpreter
可以在JSP脚本片段中间接使用Jython代码,通过嵌入
PythonInterpreter
实例。示例如下:
<!--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>
将该文件放在上下文的根目录中,确保
jython.jar
文件在上下文的
lib
目录中,在浏览器中访问http://localhost:8080/jython/interp.jsp可以看到Jython解释器的消息。但很多人认为脚本片段不是好的做法,更好的方法是使用bean类和标签库。
10.3 Jython标签库
可以使用
jythonc
将Jython标签库模块编译成Java类来创建Jython标签库。Jython标签库模块必须包含一个与模块同名(去掉
.py
扩展名)的类,该类必须子类化
javax.servlet.jsp.tagext.Tag
接口或实现该接口的Java类。
以下是一个简单的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
将
JythonTag.py
文件使用
jythonc -w %TOMCAT_HOME%\webapps\jython\WEB - INF\classes JythonTag.py
编译,然后创建一个标签库描述文件:
<?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 - XML\jython-taglibs.tld
。以下是一个使用该标签库的JSP文件示例:
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
<html>
<body>
<jython:message />
</body>
</html>
将该文件保存为
%TOMCAT_HOME%\webapps\jython\test.jsp
,在浏览器中访问http://localhost:8080/jython/test.jsp可以看到自定义标签消息。
10.4 BSF
IBM的Bean Scripting Framework(BSF)支持Jython,结合BSF标签库和BSF jar文件可以在JSP页面中快速插入Jython脚本片段和表达式。
首先下载正确的
bsf.jar
文件并放在上下文的
lib
目录中,同时将BSF标签库的jar文件和
bsf.tld
文件放在相应目录。在JSP文件中使用以下指令标识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对象会自动设置在解释器中,如
request
、
response
、
pageContext
等。以下是一个使用BSF脚本片段创建时间戳的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可以看到客户端信息和访问时间。
11. 总结与展望
在服务器端Web编程中,Jython提供了一种灵活且高效的方式来结合Python的简洁性和Java的强大生态。通过Servlet和JSP,我们可以充分利用Jython的优势,实现快速开发和资源的有效利用。
11.1 核心要点回顾
- Servlet容器 :Jython可以与多种Servlet容器配合使用,如Tomcat、Apache JServ、Jigsaw和Jetty等。其中,Tomcat是参考实现,使用广泛。
-
Servlet类
:通过简单的Java和Jython Servlet示例,我们了解了如何定义和测试Servlet。使用
jythonc编译Jython Servlet或使用PyServlet映射都是可行的部署方式。 -
关键类和方法
:
GenericServlet和HttpServlet是重要的Servlet基类,init、service和destroy方法是Servlet生命周期的关键。HttpServletRequest和HttpServletResponse用于处理客户端请求和响应。 -
Cookies和Sessions
:使用
javax.Servlet.http.Cookie类可以创建和操作cookie,而HttpRequest的getSession()方法可以创建会话,方便跟踪客户端信息。 - 数据库管理 :在jylet中管理数据库资源有两种主要方法:每个jylet一个连接或连接池。连接池可以更有效地管理资源。
-
JSP集成
:在JSP中使用Jython可以通过
jythonc编译的类、嵌入PythonInterpreter或创建Jython特定的自定义标签库来实现。
11.2 未来发展趋势
随着Web技术的不断发展,Jython在服务器端Web编程中的应用也有望不断拓展。以下是一些可能的发展趋势:
-
更广泛的框架支持
:未来可能会有更多的Web框架支持Jython,进一步简化开发流程,提高开发效率。
-
性能优化
:随着Jython和Java虚拟机的不断优化,Jython Servlet的性能可能会得到进一步提升,使其在高并发场景下更具竞争力。
-
与新兴技术的结合
:Jython可能会与新兴的Web技术,如微服务、容器化和无服务器计算等结合,为开发者提供更多的选择和可能性。
12. 实践建议
12.1 开发环境搭建
- 选择合适的Servlet容器,如Tomcat,确保其版本与所需的Servlet和JSP规范兼容。
-
配置好
TOMCAT_HOME和JAVA_HOME环境变量,方便启动和管理服务器。 -
将
jython.jar文件放置在合适的位置,如上下文的lib目录,确保Jython Servlets能够正常运行。
12.2 代码编写和调试
- 遵循Java和Jython的编程规范,确保代码的可读性和可维护性。
- 在编写Servlet时,注意处理异常,确保程序的健壮性。
- 利用调试工具,如日志记录和调试器,及时发现和解决问题。
12.3 资源管理
- 在处理数据库连接时,根据实际情况选择合适的资源管理方式,如连接池,避免资源浪费。
- 及时关闭文件、数据库连接等资源,防止资源泄漏。
12.4 安全考虑
- 在处理用户输入时,进行严格的验证和过滤,防止SQL注入、XSS攻击等安全问题。
- 合理设置cookie和会话的有效期,保护用户信息安全。
13. 常见问题解答
13.1 编译Jython Servlet时出现
ClassCastException
怎么办?
确保在编译时
CLASSPATH
包含
Servlet.jar
文件。如果仍然出现问题,检查文件名和类名是否一致,包括大小写。
13.2 如何确保Jython Servlets能够访问Jython的
lib
目录?
可以通过以下方法实现:
- 设置
python.home
属性,将其指向包含Jython模块的目录。
- 使用
jythonc
的
--deep
选项冻结所需的模块。
- 让每个Servlet显式地将模块位置附加到
sys.path
。
13.3 在JSP中使用Jython时,哪种方法最好?
这取决于具体的需求。如果需要与Java代码紧密集成,
jythonc
编译的类可能是一个不错的选择。如果希望更灵活地嵌入Jython代码,嵌入
PythonInterpreter
或使用自定义标签库可能更合适。
13.4 如何处理Servlet的异常?
在
service
方法中捕获异常,并根据异常类型进行相应的处理。可以使用日志记录异常信息,方便调试和维护。
14. 流程图总结
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
A([开始]):::startend --> B{选择开发方式}:::decision
B -->|Servlet| C(定义Servlet类):::process
B -->|JSP| D(选择JSP集成方式):::process
C --> E{选择编译方式}:::decision
E -->|jythonc编译| F(编译Jython Servlet):::process
E -->|PyServlet映射| G(配置PyServlet映射):::process
F --> H(部署Servlet):::process
G --> H
D --> I{选择集成方法}:::decision
I -->|jythonc编译的类| J(编译Jython类):::process
I -->|嵌入PythonInterpreter| K(嵌入PythonInterpreter):::process
I -->|自定义标签库| L(创建Jython标签库):::process
J --> M(在JSP中使用类):::process
K --> M
L --> M
H --> N(测试Servlet):::process
M --> O(测试JSP页面):::process
N --> P([结束]):::startend
O --> P
15. 总结表格
| 主题 | 关键信息 |
|---|---|
| Servlet容器 | Tomcat、Apache JServ、Jigsaw、Jetty等 |
| Servlet类 |
GenericServlet
、
HttpServlet
,
init
、
service
、
destroy
方法
|
| Cookies和Sessions |
javax.Servlet.http.Cookie
类,
HttpRequest
的
getSession()
方法
|
| 数据库管理 | 每个jylet一个连接或连接池 |
| JSP集成 |
jythonc
编译的类、嵌入
PythonInterpreter
、自定义标签库
|
通过以上的介绍,我们对Jython在服务器端Web编程中的应用有了全面的了解。希望这些信息能够帮助开发者更好地利用Jython的优势,开发出高效、灵活的Web应用。在实际开发中,不断实践和探索,结合具体的需求和场景,选择最合适的技术和方法。
Jython在服务器端Web编程的应用解析
超级会员免费看
49

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



