88、探索Jython在服务器端Web编程中的应用

探索Jython在服务器端Web编程中的应用

在服务器端Web编程领域,Jython有着独特的应用价值。它在许多方面与Java紧密结合,为开发者提供了更灵活、高效的开发方式。下面我们将深入探讨Jython在服务器端Web编程中的具体应用。

1. Jython在Web开发中的定位

Jython适用于Java适用的任何场景,尤其在需要快速开发和增加灵活性的Java项目中表现出色。服务器端Java Web编程主要通过Servlet和Java Server Pages(JSP)实现,因此Jython的主要应用也是Servlet和JSP。不过,Java的企业级包(j2ee)在Web应用开发中也很重要,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’s Tomcat | 是Servlet和Java Server Pages规范的参考实现,支持2.2 Servlet和1.1 JSP规范(3.2.3版本),后续版本支持更高规范。 | http://jakarta.apache.org/ |
| Apache JServ | 为Apache创建的Servlet(2.0版本)引擎,常用于与Jython结合使用。 | 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类

下面我们将比较一个简单的Java Servlet和一个简单的Jython Servlet,并介绍如何在Tomcat Web应用中安装jython.jar文件。

3.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>"); 
    } 
} 

该代码使用了Servlet API中的GenericServlet类,service方法是抽象的,必须在子类中实现,用于响应Web请求。

3.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>""") 

Jython Servlet与Java Servlet在导入、继承和实现上类似,但使用了Jython的语法。

3.3 测试Java和Jython Servlet

测试这些Servlet需要安装Tomcat,并将jython.jar文件包含在Tomcat中。以下是具体步骤:
1. 安装Tomcat
- 从http://jakarta.apache.org下载Tomcat(建议下载jakarta-tomcat-3.2.3)。
- 解压到有足够权限的目录,设置TOMCAT_HOME环境变量。
- 设置JAVA_HOME环境变量。
- 使用相应的启动脚本启动Tomcat:
- Windows:%TOMCAT_HOME%\bin\startup.bat
- nix:$TOMCAT_HOME/bin/startup.sh
- 看到“date time - PoolTcpConnector: Starting HttpConnectionHandler on 8080”表示Tomcat已启动并监听8080端口。
- 使用相应的关闭脚本停止Tomcat:
- Windows:%TOMCAT_HOME%\bin\shutdown.bat
-
nix:$TOMCAT_HOME/bin/shutdown.sh
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查看结果。
3. 安装Jython Servlet
- 有两种使用Jython Servlet的方法,这里使用jythonc编译。
- 编译Jython Servlet模块:
- 将JythonServlet.py文件放在%TOMCAT_HOME%\webapps\jython\WEB-INF\classes目录。
- 确保CLASSPATH包含servlet.jar,使用以下命令编译:jythonc -w . JythonServlet.py
- 添加jython.jar到类路径,有三种方法:
- 首选将jython.jar添加到上下文的lib目录。
- 不建议将jython.jar添加到Tomcat的lib目录。
- 将jython.jar留在Jython安装目录,但在运行Tomcat前添加到类路径。
- 使Jython的lib目录对Servlet可用,有三种方法:
- 设置python.home属性。
- 冻结所需模块。
- 让每个Servlet显式将模块位置追加到sys.path。
- 测试Jython Servlet,在浏览器中访问http://localhost:8080/jython/servlet/jythonServlet,若出现错误,可能是编译时Servlet.jar不在类路径、文件名和类名不一致或Jython模块不可用等原因。

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 |

下面是一个使用GenericServlet的HitCounter 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目录,编译后在浏览器中访问http://localhost:8080/jython/servlet/HitCounter测试。

5. HttpServlet

HttpServlet是GenericServlet的子类,更适合用于Web编程,因为它特定于HTTP协议。它为每个HTTP方法定义了方法,如doGet、doPost等。以下是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): |

下面是一个使用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目录,编译后在浏览器中访问http://localhost:8080/jython/servlet/get_post测试。

5. HttpServletRequest和HttpServletResponse

与客户端连接的通信通过HttpServletRequest和HttpServletResponse对象进行,它们为处理请求和响应提供了更高级的HTTP特定方法。
| Method and Property | Description |
| ---- | ---- |
| getAuthType() / AuthType | 返回描述认证类型名称的字符串,用户未认证时为None。 |
| getContextPath() / contextPath | 返回描述请求上下文路径信息的字符串。 |
| getCookies() / cookies() | 返回客户端请求中发送的所有cookie。 |
| getDateHeader(name) | 检索指定头的值作为长类型。 |
| getHeader(name) | 返回指定头的值作为字符串。 |
| getHeaderNames() / headerNames | 返回请求中包含的所有头名称的枚举。 |
| getHeaders(name) | 返回指定头名称的所有值的枚举。 |
| getIntHeader(name) | 检索指定头的值作为Java int。 |
| 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为true时创建新会话。 |
| getUserPrincipal() / userPrincipal | 返回包含当前认证信息的java.security.Principal对象。 |
| isRequestedSessionIdFromCookie() | 根据当前会话ID是否来自cookie返回1或0。 |
| isRequestedSessionIdFromURL() | 根据当前会话ID是否来自请求的URL字符串返回1或0。 |
| isRequestedSessionIdValid() | 根据请求的会话ID是否仍然有效返回1或0。 |
| isUserInRole(role) | 指示用户是否在指定角色中返回1或0。 |

HttpServletResponse对象用于将mime编码的流发送回客户端,它定义了一些额外的HTTP特定方法。以下是其部分方法及描述:
| Method and Property | Description |
| ---- | ---- |
| addCookie(cookie) | 向响应中添加cookie。 |
| addDateHeader(headerName, date) | 添加带有日期值的头。 |
| addHeader(headerName, value) | 添加头名称和值。 |
| addIntHeader(headerName, value) | 添加带有整数值的头。 |
| containsHeader(headerName) | 根据指定头是否存在返回1或0。 |
| encodeRedirectUrl(url) | 为sendRedirect方法编码URL。 |
| 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)方法。

6. PyServlet

对于Jython程序员来说,HttpServlet编程的一个巨大优势是Jython发行版附带了 org.python.util.PyServlet 这个Servlet。它可以加载、执行和缓存Jython文件,让你无需中间编译步骤就能编写和查看Jython Servlet。这通过Servlet映射实现,即特定Servlet(这里是PyServlet)与特定URL模式(如 *.py )的关联。借助合适的 web.xml 文件,Jython的PyServlet类会映射到所有 *.py 文件,当请求 *.py 文件时,PyServlet类会按需加载、缓存和调用 *.py 文件中的方法进行响应。

不过,PyServlet对它所服务的Jython文件有一些限制。Jython文件必须包含一个继承自 javax.servlet.http.HttpServlet 的类,且类名必须与文件名(去掉 .py 扩展名)匹配。例如, Test.py 文件必须包含名为 Test 的类,且该类必须是 javax.servlet.http.HttpServlet 的子类。此外,除了这个继承自HttpServlet的类,使用模块全局标识符是不安全的。

6.1 安装PyServlet

安装PyServlet只需定义Servlet映射,并确保 jython.jar 文件位于上下文的 lib 目录中。Servlet映射在上下文的部署描述符文件 web.xml 中定义,Jython上下文的 web.xml 文件位于 $TOMCAT_HOME/webapps/jython/WEB-INF/web.xml 。在Tomcat中,若上下文的 web.xml 未明确包含某些设置,则使用 $TOMCAT_HOME/conf/web.xml 中的默认设置。

以下是一个示例部署描述符:

<web-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定义可选择包含初始化参数( init-params ),用于在初始化Jython时设置属性,如 python.home python.path 等。例如:

<web-app> 
    <servlet> 
        <servlet-name>PyServlet</servlet-name> 
        <servlet-class> 
            org.python.util.PyServlet 
        </servlet-class> 
        <load-on-startup>1</load-on-startup> 
        <init-param> 
            <param-name>python.home</param-name> 
            <param-value>c:\jython-2.1</param-value> 
        </init-param> 
        <init-param> 
            <param-name>python.path</param-name> 
            <param-value> 
                c:\jython-2.1\lib\site-packages 
            </param-value> 
        </init-param> 
    </servlet> 
    <servlet-mapping> 
        <servlet-name>PyServlet</servlet-name> 
        <url-pattern>*.py</url-pattern> 
    </servlet-mapping> 
</web-app> 

需要注意的是,定义Jython资源位置的属性(如 python.home python.path )会影响上下文的自包含性和跨平台可移植性。PyServlet的默认 python.home 值是上下文的 lib 目录,这既保证了上下文的自包含性,又使其具有平台独立性。

6.2 测试PyServlet

可以通过启动Tomcat并在浏览器中查看一个简单的jylet(Jython - Servlet)来确认Servlet映射是否正常工作。以下是一个简单的测试jylet:

# File ServletMappingTest.py 
from javax.servlet.http import HttpServlet 
class ServletMappingTest(HttpServlet): 
    def doGet(self, req, res): 
        out = res.writer 
        res.contentType = "text/html" 
        print >> out, "Greetings from a jylet." 

将这个测试文件保存为 %TOMCAT_HOME%\webapps\jython\ServletMappingTest.py ,然后在浏览器中访问 http://localhost:8080/jython/ServletMappingTest.py 。如果看到问候消息,则说明PyServlet映射正确。为完成安装,还需创建Jython的 lib 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\lib\Lib ,并将Jython模块放在该目录中。

7. 会话管理
7.1 会话管理的基本概念

会话管理是跟踪客户端信息的一种方式,在Web应用中非常重要。Cookies是创建会话的常用手段,但Java的 HttpSession 类让会话跟踪更加方便。通过 HttpRequest getSession() 方法可以创建一个会话,返回一个 HttpSession 实例。 HttpSession 是会话管理子系统复杂行为的简单接口,在Jython中使用 HttpSession 对象与Java的区别仅在于语法和对象的 get* 方法对应的自动Bean属性。

7.2 会话管理示例

以下是一个会话管理的示例代码:

# 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都会通过 encodeUrl 方法重写,确保会话值可以通过URL传递。

8. 数据库与Servlet

在Jython Servlet中连接数据库、执行语句和遍历结果集与其他Jython数据库应用类似,但管理数据库资源是首要问题,因为许多Web应用包含多个使用数据库内容的Jylet。

8.1 数据库资源管理方式

管理数据库资源有多种选择,主要有两种方法:
- 每个Jylet一个连接 :在Jylet的 init 方法中建立数据库连接,并在Jylet卸载时关闭连接。这种方法虽然消除了响应客户端请求时的连接开销,但如果所需连接数量超出资源限制,就不太合适。
- 连接池 :连接池可以维护一定数量的活动数据库连接,Jylet在响应客户端请求时借用连接,完成后归还到池中。这可以实现资源的合理管理,消除连接开销。

8.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): 
        #define the JDBC url 
        url = "jdbc:mysql://192.168.1.77/products" 
        usr = "productsUser"     # replace with real user name 
        passwd = "secret"        # replace with real password 
        driver = "org.gjt.mm.mysql.Driver" 
        #connect to the database and get cursor object 
        self.db = zxJDBC.connect(url, usr, passwd, driver) 
        self.c = self.db.cursor() 
    def doGet(self, req, res): 
        res.setContentType("text/html") 
        out = res.getWriter() 
        print >>out, """ 
            <html> 
            <head> 
              <title>Jylet Database Connection</title> 
            </head> 
            <body> 
            <table align="center"> 
              <tr> 
                <td><b>ID</b></td> 
                <td><b>Title</b></td> 
                <td><b>Description</b></td> 
                <td><b>Price</b></td> 
                </tr>""" 
        self.c.execute("select code, name, description, price from products") 
        for row in self.c.fetchall(): 
            print >>out, """ 
                <tr> 
                  <td>%s</td> 
                  <td>%s</td> 
                  <td>%s</td> 
                  <td>%s</td>""" % row 
        print >>out, """ 
            </table> 
            </body> 
            </html>""" 
    def destroy(self): 
        self.c.close() 
        self.db.close() 

该示例假设存在名为 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

9. JSP

Java Server Pages(JSP)是Sun倡导的作为Servlet补充的模板系统。JSP文件包含网页的标记代码和文本,同时包含指定动态内容的特殊标签。目前,Tomcat仅在JSP中实现Java语言,那么如何在JSP中使用Jython呢?目前有几种方法。

9.1 使用jythonc编译的类与JSP

使用jythonc编译的类与JSP结合,需要创建一个与Java兼容的Jython类,该类的名称与模块名称相同,继承自Java类,并为每个非从超类派生的方法包含 @sig 字符串。将jythonc生成的类文件放在上下文的 classes 目录中,JSP页面就可以像使用任何原生Java类一样使用这些类。当然,这也需要将 jython.jar 文件放在上下文的 lib 目录中。

JSP文件可以通过两种方式使用jythonc编译的类:
- 作为脚本片段 :可以使用任何类,通过 <%@ page import="fully.qualified.path.to.class" %> 导入类,然后在脚本标签( <% %> )或表达式标签( <%= %> )中使用Java代码调用该类。
- 作为Bean :Jython类必须遵循Bean约定,为每个读写属性包含 getProperty setProperty 方法。使用 <jsp:useBean> 标签加载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

9.2 在JSP中嵌入PythonInterpreter

如果想在JSP脚本片段中使用Jython代码,可以通过 PythonInterpreter 实例间接实现。需要使用导入指令导入 org.python.util.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 目录中,将 interp.jsp 文件放在上下文的根目录中,在浏览器中访问 http://localhost:8080/jython/interp.jsp ,应该可以看到Jython解释器输出的消息。不过,很多人认为脚本片段不是好的做法,使用Jython在Java脚本片段中增加了复杂性,更好的方法是使用Bean类和标签库。

9.3 Jython标签库

可以使用jythonc将Jython标签库模块编译成Java类,从而在JSP页面中创建自定义标签库。Jython标签库模块必须满足一些限制才能像Java类一样透明工作。模块必须包含一个与模块名称(去掉 .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 

安装该标签库所需的类,首先确保 jython.jar 文件位于上下文的 lib 目录中,然后使用以下命令编译 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-INF\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> 
10. BSF

IBM的Bean Scripting Framework(BSF)是一个实现各种脚本语言的Java工具,Jython是目前BSF支持的语言之一。Apache Jakarta项目的 taglibs 子项目包含大量可用的Java标签库,其中有趣的是BSF标签库。BSF标签库与BSF的 jar 文件结合,让你可以快速将Jython脚本片段和表达式直接插入JSP页面。

10.1 安装和配置

下载正确的 bsf.jar 文件,将其放在上下文的 lib 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\lib 中。同时,从相关网站下载BSF标签库的 jar 文件和 bsf.tld 文件,将标签库的 jar 文件放在上下文的 lib 目录中, bsf.tld 文件放在上下文的 WEB-INF 目录 %TOMCAT_HOME%\webapps\jython\WEB-INF\bsf.tld 中。

10.2 使用Jython脚本片段

在JSP文件中使用Jython脚本片段,首先需要包含一个指令来识别BSF标签库:

<%@ taglib uri="/WEB-INF/bsf.tld" prefix="bsf" %> 

然后可以使用 bsf:scriptlet 标签,在标签体中包含Jython代码:

<bsf:scriptlet language="jython"> 
import random 
print >> out, random.randint(1, 100) 
</bsf:scriptlet> 

脚本片段中有一些JSP对象会自动设置在解释器中,包括 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: &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 ,应该可以看到客户端信息和访问时间。

综上所述,Jython在服务器端Web编程中有着丰富的应用场景,通过与Servlet、JSP等技术的结合,可以为开发者提供更灵活、高效的开发方式。无论是简单的Servlet开发,还是复杂的数据库连接和JSP集成,Jython都能发挥重要作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值