Jython在服务器端Web编程中的应用
1. Jython在Web开发中的定位
Jython适用于Java适用的任何场景,尤其在需要快速开发和提高灵活性的Java项目中表现出色。服务器端Java Web编程主要通过Servlet和Java Server Pages(JSP)实现,因此Jython的主要应用也是Servlet和JSP。不过,Java的企业级包(j2ee)在Web应用开发中也很重要,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等。
2. Jython Servlet容器
Jython可以与任何兼容的Java Servlet容器一起工作,有很多这样的容器可供选择。这里以Tomcat为例,它是Servlet和Java Server Page规范的参考实现。以下是一些流行且免费的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,这是个不错的选择。需要单独下载Java Servlet Development Kit 2.0。Java Server Pages需要一个外部模块。
- 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 方法是抽象的,必须在子类中实现。该方法在接收到对 JavaServlet 的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示例类似,继承自 GenericServlet 并实现了 service 方法。语法上有一些差异,如类定义后的括号指定超类、省略 throws 语句、 service 方法中的显式 self 参数,以及没有分号和显式类型声明。
3.3 测试Java和Jython Servlet
测试这些Servlet需要安装Tomcat,Jython Servlet还需要将 jython.jar 文件包含在Tomcat中。
3.3.1 安装Tomcat
- 从 http://jakarta.apache.org 下载Tomcat,建议下载jakarta - tomcat - 3.2.3的二进制版本。
- 将下载的文件解压到有足够权限的目录,该目录即为Tomcat主目录。设置环境变量
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指向JDK安装的根目录,例如:
# 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
在*nix系统上使用:
$TOMCAT_HOME/bin/startup.sh
启动时会看到一些信息,注意“date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080”这一行,它表示将使用的端口,默认是8080。
- 停止Tomcat,在Windows上使用:
%TOMCAT_HOME%\bin\shutdown.bat
在*nix系统上使用:
$TOMCAT_HOME/bin/shutdown.sh
3.3.2 安装Java Servlet
将 JavaServlet.java 文件放在 %TOMCAT_HOME%\webapps\jython\WEB-INF\classes 目录下,由于该Servlet不在包中,所以放在 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模块 :将 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
如果没有看到这些行,说明有问题,需要检查 CLASSPATH 和设置选项并重新编译。
2. 将jython.jar文件添加到Web应用 :有三种添加方式:
- 将 jython.jar 添加到上下文的 lib 目录,这是首选方式,能使Web应用自包含。
- 将 jython.jar 添加到Tomcat的 lib 目录,不建议使用,会使Web应用不再自包含。
- 将 jython.jar 留在Jython的安装目录,但在运行Tomcat之前将其添加到类路径中,这种方式有一定合理性,但不如放在上下文的 lib 目录。
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)
3.3.4 测试Jython Servlet
编译完成、添加 jython.jar 并使Jython模块可访问后,在浏览器中访问 http://localhost:8080/jython/servlet/jythonServlet ,应该能看到消息“This is a Servlet of the Jython variety”。如果看不到,可能会出现以下错误消息:
- 如果编译时 Servlet.jar 不在类路径中,可能会看到 ClassCastException 。
- 如果文件名和类名不同(即使只是大小写不同),也会看到 ClassCastException 。
- 如果编译生成的类文件不在上下文的 classes 目录中,会看到 AttributeError 。
- 如果Jython的模块不可用,会看到 ImportError 。
4. 关于GenericServlet
javax.Servlet.GenericServlet 是一个通用的Servlet类,不特定于任何协议。虽然 HttpServlet 在Web编程中更常用,但 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”,后续每次访问计数会增加。关闭并重启Tomcat后,计数应该继续。
4.2 生命周期方法
- init(ServletConfig)方法 :在Servlet启动时调用,仅调用一次,可用于耗时任务,如设置数据库连接、编译正则表达式或加载辅助文件。该方法可以处理有参数和无参数的
init方法。 - service(ServletRequest, ServletResponse)方法 :在每个客户端请求时调用,Servlet必须定义该方法,因为它在
GenericServlet中是抽象的。 - destroy()方法 :在关闭或卸载Servlet时调用,用于清理代码,如关闭数据库连接、刷新和关闭文件等。但该方法存在一定问题,如果服务器不正常关闭,可能不会调用该方法,导致数据丢失。
5. HttpServlet
GenericServlet 适用于任何聊天类型的协议,而Web开发主要涉及HTTP协议,因此 javax.Servlet.http.HttpServlet 更适合扩展。 HttpServlet 是 GenericServlet 的子类,因此 GenericServlet 的 init 、 service 和 destroy 方法在扩展 HttpServlet 时可用。
5.1 HttpServlet方法
HttpServlet 为每个HTTP方法定义了一个方法,如 doGet 、 doPost 、 doPut 、 doOptions 、 doDelete 和 doTrace 。当Tomcat接收到特定类型的客户端请求时,会调用相应的方法进行响应。此外, HttpServlet 还有一个特定于HTTP的 service 方法。以下是 HttpServlet 方法及其在Jython中的使用示例:
| 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 的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 ,可以看到相关页面。点击提交按钮会执行 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整数形式检索指定头的值,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 可以处理所有 .py 文件的请求。
6.1 安装PyServlet
安装 PyServlet 只需定义一个Servlet映射,并确保 jython.jar 文件在上下文的 lib 目录中。Servlet映射在上下文的部署描述符文件 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 的定义可以包含初始化参数,用于设置Jython属性,如 python.home 和 python.path 。
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 映射正确。完成安装后,创建Jython的 lib 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib ,用于放置Jython模块。
7. Cookies
Cookies允许在客户端机器上存储信息,并在后续请求中包含这些信息。在Jython中,可以使用 javax.Servlet.http.Cookie 类创建和操作Cookies。以下是一个示例:
from javax.servlet import http
name = "book1"
value = "Lewis Carroll, Alice In Wonderland"
MyNewCookie = http.Cookie(name, value)
res.addCookie(MyNewCookie)
每个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>"""
测试该Jylet时,确保浏览器允许使用Cookies,将 cookies.py 文件放在 %TOMCAT_HOME%\webapps\jython 目录下,在浏览器中访问 http://localhost:8080/jython/cookies.py ,可以添加和查看Cookies。
8. Sessions
Cookies是创建与客户端会话的最常见方式,但Java的 HttpSession 类使会话跟踪更加容易。可以使用 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>
"""
将 session.py 文件保存到 %TOMCAT_HOME%\webapps\jython 目录下,在浏览器中访问 http://localhost:8080/jython/session.py ,可以添加和查看会话变量。即使客户端禁用了Cookies,会话对象仍然可以通过URL重写工作。
9. 数据库和Servlet
在Jylet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用没有区别,但管理数据库连接和资源是一个主要问题。因为许多Web应用包含多个使用数据库内容的Jylet,每个Jylet创建数据库连接会快速消耗数据库资源,而为每个请求创建和关闭数据库连接会带来不可接受的开销。
9.1 管理数据库资源的方法
- 每个Jylet一个连接 :在Jylet的
init方法中建立数据库连接,在卸载Jylet时关闭连接。这种方法可以消除响应客户端请求时的连接开销,但只有在所需连接数量在资源限制范围内时才合理。 - 连接池 :维护一定数量的活动数据库连接,Jylet在响应客户端请求时借用连接,完成后归还。这可以实现资源的合理管理并消除连接开销。
9.2 示例
以下是一个使用数据库连接的Jylet示例:
# 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" # replace with real user name
passwd = "secret" # replace with real 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()
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()
该示例假设存在一个名为 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应用开始使用过多连接,可以考虑使用连接池,如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类,然后在脚本标签( <% %> )或表达式标签( <%= %> )中使用该类。
- 作为Bean :使用 <jsp:useBean name="beanName" class="fully.qualified path.to.class" scope="scope(page or session)"> 加载Bean,该Jython类必须符合Bean约定,为每个读写属性包含 getProperty 和 setProperty 方法。
以下是一个简单的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中
如果想在JSP脚本中使用Jython代码,可以间接使用 PythonInterpreter 实例。以下是一个简单的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 目录中,在浏览器中访问 http://localhost:8080/jython/interp.jsp ,可以看到Jython解释器的简单消息。但许多人认为脚本是不良实践,使用Jython会增加复杂度,有更好的方法创建动态内容,如Bean类和标签库。
10.3 Jython标签库
标签库是可以在JSP页面中使用的自定义标签库。可以使用jythonc将Jython标签库模块编译成Java类来创建Jython标签库。Jython标签库模块必须满足以下条件:
- 包含一个与模块名称相同(不包括 .py 扩展名)的类。
- 该类必须子类化 javax.servlet.jsp.tagext.Tag 接口或实现该接口的Java类。
- org.python.* 包和类以及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
将 JythonTag.py 文件编译成Java类后,还需要创建一个标签库描述文件,以下是示例:
<?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" %>
然后可以使用 <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 ,可以看到自定义标签消息。
10.4 BSF
IBM的Bean Scripting Framework(BSF)是一个实现各种脚本语言的Java工具,Jython是其支持的语言之一。Apache Jakarta项目的 taglibs 子项目包含一个有趣的BSF标签库,结合BSF的jar文件,可以在JSP页面中快速插入Jython脚本和表达式。
10.4.1 安装
下载正确的 bsf.jar 文件,将其放在上下文的 lib 目录中。同时,将包含BSF标签库的jar文件和 bsf.tld 文件分别放在上下文的 lib 目录和 WEB-INF 目录中。
10.4.2 使用
在JSP文件中使用Jython脚本,首先需要包含以下指令:
<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %>
然后可以使用 bsf.scriptlet 标签,例如:
<bsf.scriptlet language="jython">
import random
print >> out, random.randint(1, 100)
</bsf.scriptlet>
脚本中有一些JSP对象会自动设置在解释器中,包括 request 、 response 、 pageContext 等。以下是一个使用 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容器、Servlet类、Cookies、Sessions、数据库连接和JSP等方面,希望对你有所帮助。
10.5 不同方法使用Jython在JSP中的对比
| 使用方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| jythonc编译的类 | 可像使用本地Java类一样使用,能作为Bean或脚本使用,符合Java规范 | 需要创建与Java兼容的Jython类,包含 @sig 字符串,编译和配置相对复杂 | 需要与现有Java代码紧密集成,对性能有一定要求的场景 |
| 嵌入PythonInterpreter | 可直接在JSP中使用Jython代码 | 脚本被认为是不良实践,增加复杂度,不利于代码维护 | 快速验证Jython代码在JSP中的可行性,对代码结构要求不高的场景 |
| Jython标签库 | 可创建自定义标签,提高代码复用性和可维护性 | 需要创建标签库模块和描述文件,配置较复杂 | 有大量重复代码,需要统一管理和复用的场景 |
| BSF | 可快速插入Jython脚本和表达式,利用现有JSP对象 | 需要下载和配置相关文件,依赖特定的标签库 | 需要在JSP中频繁使用Jython脚本,且希望利用现有JSP对象的场景 |
10.6 Jython在JSP中使用的注意事项
- 文件路径和类路径 :确保jythonc编译的类文件、jython.jar文件、标签库描述文件等都放在正确的目录中,并且类路径配置正确,否则会出现类找不到或无法加载的错误。
-
@sig字符串 :在使用jythonc编译的类时,为每个非从超类派生的方法包含@sig字符串,以确保方法签名正确,避免运行时错误。 - 循环引用问题 :在使用jythonc编译的类时,要注意避免循环引用,特别是在处理自动Bean属性时,可能会导致
StackOverflowException。 - 脚本实践 :虽然可以使用嵌入PythonInterpreter或BSF在JSP中使用Jython脚本,但脚本被认为是不良实践,尽量使用Bean类和标签库来创建动态内容,提高代码的可维护性。
11. 总结与最佳实践
11.1 总结
本文详细介绍了Jython在服务器端Web编程中的应用,涵盖了Servlet容器、Servlet类、Cookies、Sessions、数据库连接和JSP等多个方面。通过使用Jython,开发者可以结合Java的强大功能和Python的灵活性,提高开发效率和代码的可维护性。
11.2 最佳实践
- 选择合适的Servlet容器 :根据项目需求和团队经验,选择合适的Servlet容器,如Tomcat、Apache JServ、Jigsaw或Jetty。
- 合理使用Servlet类 :根据协议类型选择合适的Servlet类,如
GenericServlet适用于通用协议,HttpServlet适用于HTTP协议。 - 有效管理Cookies和Sessions :使用Cookies和
HttpSession类来管理客户端信息,确保会话的连续性和数据的安全性。 - 优化数据库连接 :根据项目规模和资源需求,选择合适的数据库连接管理方式,如每个Jylet一个连接或连接池。
- 谨慎使用JSP中的Jython :在JSP中使用Jython时,选择合适的方式,如jythonc编译的类、Jython标签库或BSF,避免使用脚本,提高代码的可维护性。
11.3 未来发展趋势
随着Web开发技术的不断发展,Jython在服务器端Web编程中的应用可能会更加广泛。未来可能会出现更多与Jython集成的工具和框架,进一步提高开发效率和代码质量。同时,随着对性能和安全性的要求不断提高,Jython在数据库连接管理、会话管理等方面的优化也将成为研究的重点。
12. 流程图示例
12.1 Jython Servlet安装流程
graph TD;
A[下载Tomcat] --> B[解压到指定目录];
B --> C[设置TOMCAT_HOME和JAVA_HOME环境变量];
C --> D[启动Tomcat];
D --> E[创建Jython上下文目录结构];
E --> F[安装Java Servlet];
E --> G[安装Jython Servlet];
G --> H[使用jythonc编译Jython Servlet模块];
G --> I[将jython.jar文件添加到Web应用];
G --> J[使Jython的lib目录对Servlet可用];
F --> K[编译Java Servlet文件];
H --> L[检查编译结果];
I --> M[选择合适的添加方式];
J --> N[选择合适的方法];
K --> O[测试Java Servlet];
L --> P[测试Jython Servlet];
12.2 JSP中使用Jython的流程
graph TD;
A[选择使用方式] --> B{jythonc编译的类};
A --> C{嵌入PythonInterpreter};
A --> D{Jython标签库};
A --> E{BSF};
B --> F[创建与Java兼容的Jython类];
B --> G[将类文件放在上下文的classes目录];
B --> H[在JSP中使用类];
C --> I[导入PythonInterpreter];
C --> J[在JSP中执行Jython代码];
D --> K[创建Jython标签库模块];
D --> L[编译标签库模块];
D --> M[创建标签库描述文件];
D --> N[在JSP中使用标签];
E --> O[下载和配置相关文件];
E --> P[在JSP中使用BSF标签];
F --> Q[包含@sig字符串];
G --> R[确保jython.jar文件在lib目录];
H --> S[选择作为Bean或脚本使用];
K --> T[满足标签库模块要求];
M --> U[配置标签库描述文件];
O --> V[下载bsf.jar文件和标签库];
P --> W[使用JSP对象];
13. 常见问题解答
13.1 安装和配置问题
- 问题 :安装Tomcat时,启动报错,提示找不到Java或环境变量配置错误。
解答 :检查JAVA_HOME环境变量是否正确设置,指向JDK安装的根目录。同时,确保PATH环境变量中包含JDK的bin目录。 - 问题 :使用jythonc编译Jython Servlet模块时,出现
ClassCastException。
解答 :检查编译时Servlet.jar是否在类路径中,确保CLASSPATH环境变量包含Servlet.jar的路径。另外,检查文件名和类名是否一致,包括大小写。 - 问题 :在JSP中使用jythonc编译的类时,出现类找不到的错误。
解答 :检查类文件是否放在上下文的classes目录中,并且jython.jar文件是否在上下文的lib目录中。同时,确保JSP文件中导入类的路径正确。
13.2 代码运行问题
- 问题 :在使用Cookies时,无法设置或获取Cookies。
解答 :确保浏览器允许使用Cookies,并且在发送其他数据到输出流之前,使用req.session或req.getSession()方法获取会话对象。另外,检查addCookie方法是否正确调用。 - 问题 :在使用数据库连接时,出现连接超时或无法连接的错误。
解答 :检查数据库的URL、用户名、密码和驱动是否正确,确保数据库服务正常运行。同时,检查连接池的配置是否合理,避免连接过多或过少。 - 问题 :在使用JSP中的Jython脚本时,出现语法错误或对象找不到的错误。
解答 :检查Jython脚本的语法是否正确,确保使用的JSP对象名称和类型正确。另外,检查相关的标签库和类路径是否配置正确。
13.3 性能和优化问题
- 问题 :Web应用响应缓慢,性能不佳。
解答 :可以从以下几个方面进行优化:优化数据库连接管理,使用连接池减少连接开销;优化代码逻辑,避免不必要的计算和循环;使用缓存技术,减少重复计算;对静态资源进行压缩和合并,减少网络传输时间。 - 问题 :Jython代码运行效率低。
解答 :可以使用jythonc编译Jython代码,将其转换为Java字节码,提高运行效率。同时,优化代码结构,避免使用过于复杂的算法和数据结构。
14. 示例代码汇总
14.1 简单的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>");
}
}
14.2 简单的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>""")
14.3 计数器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()
14.4 使用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
14.5 会话管理Servlet
# 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>
"""
14.6 使用数据库连接的Jylet
# 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" # replace with real user name
passwd = "secret" # replace with real 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()
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()
14.7 简单的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
14.8 使用Jython 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>
14.9 嵌入PythonInterpreter的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>
14.10 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
14.11 Jython标签库描述文件
<?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>
14.12 使用Jython标签库的JSP文件
<%@ taglib uri="/WEB-INF/jython-taglibs.tld" prefix="jython" %>
<html>
<body>
<jython:message />
</body>
</html>
14.13 使用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>
通过以上对Jython在服务器端Web编程中各个方面的详细介绍和示例代码,希望能帮助你更好地理解和应用Jython,在实际项目中发挥其优势,提高开发效率和代码质量。如果你在实践过程中遇到任何问题,可以参考常见问题解答部分,或者查阅相关文档进行进一步的学习和探索。
超级会员免费看
49

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



