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

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 &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>""" 

测试该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: &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容器、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: &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> 

通过以上对Jython在服务器端Web编程中各个方面的详细介绍和示例代码,希望能帮助你更好地理解和应用Jython,在实际项目中发挥其优势,提高开发效率和代码质量。如果你在实践过程中遇到任何问题,可以参考常见问题解答部分,或者查阅相关文档进行进一步的学习和探索。

内容概要:本文介绍了基于Koopman算子理论的模型预测控制(MPC)方法,用于非线性受控动力系统的状态估计与预测。通过将非线性系统近似为线性系统,利用数据驱动的方式构建Koopman观测器,实现对系统动态行为的有效建模与预测,并结合Matlab代码实现具体仿真案例,展示了该方法在处理复杂非线性系统中的可行性与优势。文中强调了状态估计在控制系统中的关键作用,特别是面对不确定性因素时,Koopman-MPC框架能够提供更为精确的预测性能。; 适合人群:具备一定控制理论基础和Matlab编程能力的研【状态估计】非线性受控动力系统的线性预测器——Koopman模型预测MPC(Matlab代码实现)究生、科研人员及从事自动化、电气工程、机械电子等相关领域的工程师;熟悉非线性系统建模与控制、对先进控制算法如MPC、状态估计感兴趣的技术人员。; 使用场景及目标:①应用于非线性系统的建模与预测控制设计,如机器人、航空航天、能源系统等领域;②用于提升含不确定性因素的动力系统状态估计精度;③为研究数据驱动型控制方法提供可复现的Matlab实现方案,促进理论与实际结合。; 阅读建议:建议读者结合提供的Matlab代码逐段理解算法实现流程,重点关注Koopman算子的构造、观测器设计及MPC优化求解部分,同时可参考文中提及的其他相关技术(如卡尔曼滤波、深度学习等)进行横向对比研究,以深化对该方法优势与局限性的认识。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值