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也有不适用的场景。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规范的参考实现,稳定版本3.2.3支持2.2 Servlet和1.1 JSP规范,后续会有4.0版本支持2.3 Servlet和1.2 JSP规范。 | http://jakarta.apache.org/ |
| Apache JServ | 为Apache创建的Servlet(版本2.0)引擎,常用部署方式。 | http://java.apache.org/ |
| Jigsaw | W3C的实验性Web服务器,是完全符合HTTP/1.1的Web服务器和缓存代理服务器,支持Servlet 2.2规范和Java Server Pages 1.1。 | http://www.w3.org/Jigsaw/ |
| Jetty | 紧凑高效的Java Web服务器,支持Servlet 2.2和JSP 1.1规范,支持HTTP 1.1,包括SSL支持,可轻松与EJB服务器集成。 | http://jetty.mortbay.com/ |
这些工具的文档很丰富,安装指南可从各自的网站获取。
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>");
}
}
这个示例使用了Servlet API中的GenericServlet类,
service
方法是抽象的,必须在子类中实现,用于响应Web请求。
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类似,Jython Servlet也继承自GenericServlet类并实现了
service
方法。不过,Jython有一些语法差异,如类定义后的括号、省略
throws
语句、显式的
self
参数、无分号和显式类型声明等。
3.3 测试Java和Jython Servlet
测试这些Servlet需要安装Tomcat,并将
jython.jar
文件包含在Tomcat中。具体步骤如下:
3.3.1 安装Tomcat
- 从http://jakarta.apache.org 下载Tomcat,建议下载jakarta - tomcat - 3.2.3版本。
-
将下载的文件解压到有足够权限的目录,设置
TOMCAT_HOME环境变量。-
Windows系统:
set TOMCAT_HOME=c:\jakarta - tomcat - 3.2.3 -
*nix系统:
export TOMCAT_HOME=/usr/local/jakarta - tomcat3.2.3
-
Windows系统:
-
设置
JAVA_HOME环境变量,例如使用JDK1.3.1:-
Windows系统:
set JAVA_HOME=c:\jdk1.3.1 -
*nix系统:
export JAVA_HOME=/usr/java/jdk1.3.1
-
Windows系统:
-
启动Tomcat:
-
Windows系统:
%TOMCAT_HOME%\bin\startup.bat -
*nix系统:
$TOMCAT_HOME/bin/startup.sh -
当看到
date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080时,Tomcat正在运行并准备在8080端口接受连接。
-
Windows系统:
-
停止Tomcat:
-
Windows系统:
%TOMCAT_HOME%\bin\shutdown.bat -
*nix系统:
$TOMCAT_HOME/bin/shutdown.sh
-
Windows系统:
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.
编译Jython Servlet模块
:
- 将
JythonServlet.py
文件放在
%TOMCAT_HOME%\jython\WEB - INF\classes
目录。
- 确保
CLASSPATH
包含
Servlet.jar
,然后在该目录下执行:
jythonc - w . JythonServlet.py
- 编译时要确保看到以下行:
Creating .java files:
JythonServlet module
JythonServlet extends javax.servlet.GenericServlet
-
将
jython.jar添加到类路径 :有三种方法,推荐将jython.jar添加到上下文的lib目录。-
添加到上下文的
lib目录:%TOMCAT_HOME%\webapps\jython\WEB - INF\lib -
添加到Tomcat的
lib目录:不推荐,会使Web应用不再自包含。 - 留在Jython安装目录,在运行Tomcat前添加到类路径。
-
添加到上下文的
-
使Jython的
lib目录对Servlet可用 :有三种方法:-
设置
python.home属性 :创建%TOMCAT_HOME%\webapps\jython\WEB - INF\jylib目录,设置TOMCAT_OPTS环境变量。-
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系统:
-
显式添加模块目录到
sys.path:
-
设置
import sys
libpath = "c:/jakarta - tomcat_3.2.3/webapps/jython/WEB - INF/jylibs/Lib"
sys.path.append(libpath)
- **冻结所需模块**:在Jython上下文的`classes`目录下执行:`jythonc - w . --deep JythonServlet.py`
测试Jython Servlet时,在浏览器中访问
http://localhost:8080/jython/servlet/jythonServlet
,应看到消息
This is a Servlet of the Jython variety
。如果看不到,可能是
Servlet.jar
不在类路径、文件名和类名不一致或Jython模块不可用等原因。
4. 关于GenericServlet
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"
|
|
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
目录,编译并测试。该Servlet的
init
方法在启动时初始化计数器,
service
方法处理每个客户端请求,
destroy
方法在关闭或卸载Servlet时将计数器值写入文件。
4.2 各方法的作用
-
init(ServletConfig)方法 :在Servlet启动时调用,仅调用一次,适用于耗时任务,如设置数据库连接、编译正则表达式或加载辅助文件。 -
service(ServletRequest, ServletResponse)方法 :响应每个客户端请求,必须在子类中定义,接收ServletRequest和ServletResponse对象。 -
destroy()方法 :在关闭或卸载Servlet时调用,用于清理代码,如关闭数据库连接、刷新和关闭文件等。
5. HttpServlet
HttpServlet
是
GenericServlet
的子类,特定于HTTP协议。它为每个HTTP方法定义了方法,如
doGet
、
doPost
、
doPut
等。当Tomcat接收到GET类型的客户端请求时,会调用请求的Servlet的
doGet
方法进行响应。
5.1 HttpServlet方法
| 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):
|
5.2 HttpServlet示例
以下是一个使用
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
目录,编译并测试。该示例展示了如何根据客户端请求类型调用不同的方法,以及如何获取请求参数。
5.3 HttpServletRequest和HttpServletResponse
与客户端连接的通信通过
HttpServletRequest
和
HttpServletResponse
对象进行。以下是
HttpServletRequest
的一些方法和属性:
| 方法和属性 | 描述 |
| — | — |
|
getAuthType()
/
AuthType
| 返回描述认证类型的字符串,用户未认证时为
None
。 |
|
getContextPath()
/
contextPath
| 返回标识请求上下文的路径信息字符串。 |
|
getCookies()
/
cookies()
| 返回客户端请求中发送的所有cookie。 |
|
getDateHeader(name)
| 以长类型检索指定头的值。 |
|
getHeader(name)
| 以字符串形式返回指定头的值。 |
|
getHeaderNames()
/
headerNames
| 返回请求中包含的所有头名称的枚举。 |
|
getHeaders(name)
| 返回指定头名称的所有值的枚举。 |
|
getIntHeader(name)
| 以Java整数形式检索指定头的值,Jython转换为
PyInteger
。 |
|
getMethod()
/
method
| 返回请求类型的字符串。 |
|
getPathInfo()
/
pathInfo
| 客户端发送的所有额外路径信息。 |
|
getPathTranslated()
/
pathTranslated
| 返回从客户端请求的额外路径信息派生的真实路径。 |
|
getQueryString()
/
queryString
| 返回客户端请求的查询字符串。 |
|
getRemoteUser()
/
remoteUser
| 返回客户端的登录名,客户端未认证时为
None
。 |
|
getRequestedSessionId()
/
requestedSessionId
| 返回客户端的会话ID。 |
|
getRequestURI()
/
requestURI
| 返回协议名称和查询字符串之间的部分。 |
|
getServletPath()
/
servletPath
| 返回指定当前Servlet的URL部分。 |
|
getSession()
/
session
| 返回当前会话,需要时创建。 |
|
getSession(create)
| 如果会话存在则返回,否则根据
create
值决定是否创建新会话。 |
|
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编码的流发送回客户端,定义了一些额外的HTTP特定方法。以下是其部分方法和属性:
| 方法和属性 | 描述 |
| — | — |
|
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 |
6. PyServlet
Jython发行版包含
org.python.util.PyServlet
,这是Jython程序员进行
HttpServlet
编程的一大优势。
PyServlet
可以加载、执行和缓存Jython文件,无需中间编译步骤。它通过Servlet映射工作,将特定的Servlet(
PyServlet
)与特定的URL模式(如
*.py
)关联起来。
6.1 PyServlet的限制
Jython文件必须包含一个继承自
javax.servlet.http.HttpServlet
的类,类名必须与文件名(不含
.py
扩展名)匹配。此外,除了继承自
HttpServlet
的类,使用模块全局标识符是不安全的。
6.2 安装PyServlet
安装
PyServlet
只需定义Servlet映射并确保
jython.jar
文件在上下文的
lib
目录中。Servlet映射在上下文的部署描述符文件
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定义可以包含初始化参数,如
python.home
和
python.path
。默认情况下,
python.home
的值是上下文的
lib
目录,这使得上下文自包含且跨平台中立。
6.3 测试PyServlet
可以通过启动Tomcat并在浏览器中查看简单的Jython Servlet来确认Servlet映射是否工作。以下是一个简单的测试文件:
# 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
。如果看到消息
Greetings from a jylet.
,则
PyServlet
映射正确。最后,创建Jython的
lib
目录
%TOMCAT_HOME%\webapps\jython\WEB - INF\lib\Lib
,安装完成。
7. Cookies
Cookies允许在客户端机器上存储信息,以便在后续请求中包含。在Jython中,可以使用
javax.Servlet.http.Cookie
类创建和操作Cookies。以下是一个创建Cookie的示例:
from javax.servlet import http
name = "book1"
value = "Lewis Carroll, Alice In Wonderland"
MyNewCookie = http.Cookie(name, value)
res.addCookie(MyNewCookie)
添加Cookies应在通过响应流发送其他内容之前进行。每个Cookie实例都有方法来设置其具体属性,如
comment
、
domain
、
maxAge
等。
以下是一个使用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>"""
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>"""
测试该示例时,确保浏览器允许Cookies,将
cookies.py
文件放在
%TOMCAT_HOME%\webapps\jython
目录,在浏览器中访问
http://localhost:8080/jython/cookies.py
,添加Cookie后确认是否成功。
8. Sessions
Cookies是与客户端创建会话的最常见方式,会话用于跟踪一系列请求中的客户端信息。不过,Java的
HttpSession
类使会话跟踪更加容易。可以使用
HttpRequest
的
getSession()
方法创建会话:
sess = req.session
以下是一个会话管理的示例:
# 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重写的效果。
9. 数据库与Servlet
在Jython Servlet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用程序没有区别。但管理数据库资源是一个主要问题,因为许多Web应用程序包含多个使用数据库内容的Jython Servlet。常见的数据库资源管理方法有两种:
-
每个Jython Servlet一个连接
:在Jython Servlet的
init
方法中建立数据库连接,仅在Servlet卸载时关闭。这种方法资源消耗大,仅在所需连接数在资源限制内时合理。
-
连接池
:维护一定数量的活动数据库连接,Jython Servlet在响应客户端请求时借用连接,完成后归还。这是更谨慎的资源管理方式。
以下是一个使用数据库连接的Jython Servlet示例:
# 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()
要使用该示例,需要创建数据库和表:
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应用程序使用过多连接,可以考虑使用连接池,如PoolMan。
10. JSP
Java Server Pages(JSP)是Sun推荐的与Servlet互补的模板系统。目前,Tomcat仅在JSP中实现Java语言,要在JSP中使用Jython,可以使用
jythonc
编译的类、嵌入
PythonInterpreter
或创建Jython特定的自定义标签库。
10.1 jythonc编译的类与JSP
使用
jythonc
编译的类与JSP需要创建与Java兼容的Jython类,该类与模块同名、继承自Java类,并为每个非从超类派生的方法包含
@sig
字符串。将
jythonc
生成的类文件放在上下文的
classes
目录中,JSP页面就可以像使用任何本地Java类一样使用这些类。
JSP文件可以通过两种方式使用
jythonc
编译的类:
-
作为脚本片段
:使用
<%@ page import="fully.qualified.path.to.class" %>
导入类,然后在脚本片段标签(
<% %>
)或表达式标签(
<%= %>
)中使用。
-
作为Bean
:使用
<jsp:useBean name="beanName" class="fully.qualified path.to.class" scope="scope(page or session)">
加载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
将
NameHandler.py
文件放在
%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>
将
interp.jsp
文件放在上下文的根目录,确保
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>
将
test.jsp
文件保存到
%TOMCAT_HOME%\webapps\jython
目录,在浏览器中访问
http://localhost:8080/jython/test.jsp
,应看到自定义标签消息。
10.4 BSF
IBM的Bean Scripting Framework(BSF)是一个实现各种脚本语言的Java工具,Jython是其支持的语言之一。要在JSP中使用Jython脚本片段,需要下载正确的
bsf.jar
文件和相关的标签库文件,并将它们放在上下文的
lib
目录和
WEB - INF
目录中。
在JSP文件中使用Jython脚本片段的示例如下:
<%@ 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实现高效、灵活的Web应用开发。在实际应用中,需要根据具体需求选择合适的方法和工具,合理管理资源,以提高应用程序的性能和可维护性。
11. 总结与建议
在服务器端Web编程中,Jython提供了一种结合Python灵活性和Java强大功能的有效方式。以下是一些总结和建议,帮助你更好地利用Jython进行Web开发:
11.1 选择合适的Servlet容器
不同的Servlet容器有不同的特点和适用场景。例如,Tomcat是Servlet和JSP规范的参考实现,稳定且功能丰富;Jetty则紧凑高效,适合对性能要求较高的场景。在选择时,需要考虑项目的规模、性能需求以及团队的技术栈。
11.2 合理使用Jython Servlet
-
编译与非编译方式
:使用
jythonc编译Jython Servlet可以提高性能,但增加了编译步骤;而使用PyServlet则无需编译,开发更便捷。可以根据项目的开发阶段和性能要求选择合适的方式。 - 遵循命名和结构规范 :在编写Jython Servlet时,要遵循命名规范,确保类名与文件名匹配,并且只使用安全的模块全局标识符。
11.3 有效管理数据库资源
- 连接方式选择 :根据项目的并发量和资源限制,选择合适的数据库连接方式。如果并发量较低,可以考虑每个Servlet一个连接;如果并发量较高,连接池是更好的选择。
- 连接池工具 :选择成熟、稳定的连接池工具,如PoolMan,以确保数据库连接的高效管理。
11.4 利用JSP的优势
-
选择合适的集成方式
:在JSP中使用Jython时,可以根据具体需求选择
jythonc编译的类、嵌入PythonInterpreter或创建自定义标签库等方式。 - 避免脚本片段滥用 :虽然脚本片段可以实现动态内容,但会使JSP页面变得复杂,建议优先使用Bean类和标签库来创建动态内容。
12. 未来趋势与展望
随着Web技术的不断发展,Jython在服务器端Web编程中的应用也可能会发生变化。以下是一些可能的未来趋势:
12.1 与新兴技术的融合
Jython可能会与新兴的Web技术,如微服务、容器化和无服务器架构等进行融合。例如,将Jython Servlet封装成微服务,通过容器化技术进行部署,实现更高的可扩展性和灵活性。
12.2 性能优化
随着Jython和Java虚拟机的不断发展,Jython的性能可能会得到进一步提升。未来可能会出现更多的性能优化技术和工具,使Jython在服务器端Web编程中更加高效。
12.3 社区支持和生态系统发展
随着Jython在Web开发中的应用越来越广泛,社区支持和生态系统可能会得到进一步发展。更多的开源项目、工具和文档可能会出现,为开发者提供更好的支持。
13. 常见问题与解决方案
在使用Jython进行服务器端Web编程时,可能会遇到一些常见问题。以下是一些常见问题及其解决方案:
13.1 编译问题
-
问题描述
:使用
jythonc编译Jython Servlet时,可能会出现编译错误,如找不到类或方法等。 -
解决方案
:确保
CLASSPATH包含所需的类库,如Servlet.jar和jython.jar。检查代码中是否存在语法错误或命名错误。
13.2 类路径问题
- 问题描述 :在运行Jython Servlet时,可能会出现类路径错误,导致找不到所需的类。
-
解决方案
:确保
jython.jar文件在正确的位置,如上下文的lib目录。检查TOMCAT_HOME和JAVA_HOME环境变量是否正确设置。
13.3 数据库连接问题
- 问题描述 :在使用数据库连接时,可能会出现连接失败或连接超时等问题。
- 解决方案 :检查数据库的配置信息,如URL、用户名和密码是否正确。确保数据库服务正常运行。如果使用连接池,检查连接池的配置是否正确。
13.4 JSP集成问题
- 问题描述 :在JSP中使用Jython时,可能会出现找不到类或方法、脚本片段执行错误等问题。
-
解决方案
:确保
jythonc编译的类文件在正确的位置,并且JSP页面正确导入了所需的类。检查脚本片段中的代码是否存在语法错误。
14. 示例项目实践
为了帮助你更好地理解和应用上述知识,下面给出一个简单的示例项目,展示如何使用Jython进行服务器端Web编程。
14.1 项目需求
创建一个简单的Web应用程序,用于展示图书信息。用户可以查看图书列表,并添加新的图书信息。
14.2 项目结构
webapps/
└── jython/
├── WEB-INF/
│ ├── classes/
│ │ ├── BookServlet.py
│ │ └── Book.py
│ ├── lib/
│ │ ├── jython.jar
│ │ └── zxJDBC.jar
│ └── web.xml
├── index.jsp
└── add_book.jsp
14.3 代码实现
14.3.1 Book.py
# Book.py
class Book:
def __init__(self, id, title, author):
self.id = id
self.title = title
self.author = author
def get_id(self):
return self.id
def get_title(self):
return self.title
def get_author(self):
return self.author
14.3.2 BookServlet.py
# BookServlet.py
from javax.servlet import http
from com.ziclix.python.sql import zxJDBC
class BookServlet(http.HttpServlet):
def init(self, cnfg):
url = "jdbc:mysql://localhost:3306/bookstore"
usr = "root"
passwd = "password"
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()
out.println("<html><body>")
out.println("<h2>Book List</h2>")
self.c.execute("SELECT id, title, author FROM books")
for row in self.c.fetchall():
book = Book(row[0], row[1], row[2])
out.println("<p>ID: %s, Title: %s, Author: %s</p>" % (book.get_id(), book.get_title(), book.get_author()))
out.println("<a href='add_book.jsp'>Add Book</a>")
out.println("</body></html>")
def doPost(self, req, res):
title = req.getParameter("title")
author = req.getParameter("author")
self.c.execute("INSERT INTO books (title, author) VALUES (%s, %s)", (title, author))
self.db.commit()
res.sendRedirect("BookServlet")
def destroy(self):
self.c.close()
self.db.close()
14.3.3 index.jsp
<!-- index.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>Bookstore</title>
</head>
<body>
<h1>Welcome to the Bookstore</h1>
<a href="BookServlet">View Book List</a>
</body>
</html>
14.3.4 add_book.jsp
<!-- add_book.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>Add Book</title>
</head>
<body>
<h2>Add a New Book</h2>
<form action="BookServlet" method="post">
<label for="title">Title:</label>
<input type="text" id="title" name="title" required><br>
<label for="author">Author:</label>
<input type="text" id="author" name="author" required><br>
<input type="submit" value="Add Book">
</form>
</body>
</html>
14.4 部署与测试
- 将上述代码文件按照项目结构放置到相应的目录中。
-
确保
jython.jar和zxJDBC.jar文件在WEB-INF/lib目录中。 - 启动Tomcat服务器。
-
在浏览器中访问
http://localhost:8080/jython/index.jsp,即可看到图书列表页面。 - 点击“Add Book”链接,进入添加图书页面,输入图书信息并提交,即可添加新的图书。
13. 总结
Jython在服务器端Web编程中提供了丰富的功能和灵活的开发方式。通过合理选择Servlet容器、有效管理数据库资源、利用JSP的优势以及解决常见问题,开发者可以构建出高效、可维护的Web应用程序。同时,关注未来趋势和进行示例项目实践,有助于不断提升开发技能和应对新的挑战。希望本文能为你在服务器端Web编程中使用Jython提供有价值的参考。
超级会员免费看
49

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



