87、Jython在服务器端Web编程中的应用

Jython在服务器端Web编程的应用解析

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 
  1. 设置 JAVA_HOME 环境变量,例如:
# on Windows 
set JAVA_HOME=c:\jdk1.3.1 
# bash (*nix) setup 
export JAVA_HOME=/usr/java/jdk1.3.1 
  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 &nbsp; 
                <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: &nbsp;%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应用。在实际开发中,不断实践和探索,结合具体的需求和场景,选择最合适的技术和方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值