在服务器端运行JavaScript文件(二)

 在前回的译文中介绍了在Java中怎样调用和执行JavaScript脚本,以及怎样实现JavaScript脚本的缓存机制,在接下的译文中,我们是用将使用前面提到的ScriptCache类,实现它的abstract getScriptFile()方法,并且使用getScript()方法从缓存中取得被编译的脚本。

创建一个脚本运行器

   在这节中,将学习怎样创建一个简单的实现URL-script映射的Java servlet,这个servlet允许来自于浏览器的服务器端的脚本调用。另外,这个servlet将向JavaScript公开几个能够在JavaScript代码中使用的Java EE对象变量。
初始化servlet
      Servlet类的名字是JSServlet,它的init()放(代码10)取得几个配置参数,并且创建一个ScriptCache对象。这个servelt的脚本缓存使用getRealPath()方法获取映射个URI的脚本文件的路径。

代码10

  1. package jsee.servlet;
  2. import javax.script.*;
  3. import javax.servlet.*;
  4. import javax.servlet.http.*;
  5. import java.io.*;
  6. import jsee.cache.*;
  7. public class JSServlet extends HttpServlet {
  8.     private String cacheControlHeader;
  9.     private String contentTypeHeader;
  10.     private ScriptCache scriptCache;
  11.     
  12.     public void init() throws ServletException {
  13.         ServletConfig config = getServletConfig();
  14.         cacheControlHeader = config.getInitParameter("Cache-Control");
  15.         contentTypeHeader = config.getInitParameter("Content-Type");
  16.         int maxCachedScripts = Integer.parseInt(
  17.                 config.getInitParameter("Max-Cached-Scripts"));
  18.         scriptCache = new ScriptCache(maxCachedScripts) {
  19.             public File getScriptFile(String uri) {
  20.                 return new File(getServletContext().getRealPath(uri));
  21.             }
  22.         };
  23.     }
  24.     ...
  25. }

   下面列出了在web.xml文件中指定的servelet的参数,这文件中的Cache-Control标题与脚本缓存无关,这两个标题是servlet返回的HTTP响应的一部分。no-cache值将告诉浏览器不要缓存servelt的响应,内容类型应该作为text/plain来处理。

web.xml文件

  1. <web-app ...>

  2.     <servlet>
  3.         <servlet-name>JSServlet</servlet-name>
  4.         <servlet-class>jsee.servlet.JSServlet</servlet-class>
  5.         <init-param>
  6.             <param-name>Cache-Control</param-name>
  7.             <param-value>no-cache</param-value>
  8.         </init-param>
  9.         <init-param>
  10.             <param-name>Content-Type</param-name>
  11.             <param-value>text/plain</param-value>
  12.         </init-param>
  13.         <init-param>
  14.             <param-name>Max-Cached-Scripts</param-name>
  15.             <param-value>1000</param-value>
  16.         </init-param>
  17.         <load-on-startup>1</load-on-startup>
  18.     </servlet>

  19.     <servlet-mapping>
  20.         <servlet-name>JSServlet</servlet-name>
  21.         <url-pattern>*.jss</url-pattern>
  22.     </servlet-mapping>

  23. </web-app>

   在上面的web.xml文件中,我们看到了映射给servlet*.jss样式,这就意味着JSServlet将处理所有的以.jss为扩展名的URLs请求。当用户在浏览器的URL地址栏或点击一个*.jss的链接时,浏览器都会给Web服务器(如:Apache)发送一个HTTP请求,这个请求会根据配置信息派发给对应的servlet容器(如:Tomcat),如果servlet容器作为Web服务器,就不需要额外的配置信息。

   当servlet获得以*.jss结尾的URL请求时,就会调用JSServlet的从javax.servlet.http.HttpServlet中继承的service()方法。这个方法即可以调用doGet()方法,也可以调用doPost()方法,这依赖于HTTP请求的方法。在本节的最后,我们会看到在JSServlet中,这两个方法都被重写了。

使用脚本环境

   每个脚本引擎实例都有一个默认的环境,在这个环境中可以使用put()方法来保存变量,而且默认的情况下可以用System.out输出任意可执行的脚本。在服务器环境中,你会想让每个当前运行的脚本彼此有自己环境,java.script API可以满足这个需要,它提供了ScriptContext接口并且有SimpleScriptContext的实现。

      Mozilla’s Rhino JavaScript引擎是一个多线程引擎,允许执行当前共享的相同环境的脚本。但是,在我们的案例中,想要独立的引擎范围,而且要在不同的线程中运行脚本,这就要求必须为每个HTTP请求创建一个新的ScriptContext接口。

    代码12展示了JSServlet类的createScriptContext()方法。在脚本被执行的时候,为了把脚本的输出发送给response对象的writer方法,createScriptContext()中设定环境的writer属性。这意味着在脚本中传递给print()println()方法的每件事情都将包含servlet的响应。

    另外,createScriptContext()方法用脚本环境的的setAttribute()方法定义了下表中的脚本变量。

1:在JSServlet执行的脚本中有效的变量

脚本变量

描述

config

Servletjavax.servlet.ServletConfig实例

application

Web应用的javax.servlet.ServletContext实例

session

javax.servlet.http.HttpSession对象

request

javax.servlet.http.HttpServletRequest对象

response

javax.servlet.http.HttpServletResponse对象

out

用于输出响应的java.io.PrintWriter对象

factory

脚本引擎的javax.scriptEngineFactory实例


     factory变量能够用来获取JavaScript引擎的信息,如语言的版本或引擎的版本。其他的变量在JSP页中都有相同的对应的角色。

代码12JSServlet类的createScriptContext()方法
  1. public class JSServlet extends HttpServlet {
  2.     ...
  3.     protected ScriptContext createScriptContext(
  4.             HttpServletRequest request, HttpServletResponse response)
  5.             throws IOException {
  6.         ScriptContext scriptContext = new SimpleScriptContext();
  7.         scriptContext.setWriter(response.getWriter());
  8.         int scope = ScriptContext.ENGINE_SCOPE;
  9.         scriptContext.setAttribute("config", getServletConfig(), scope);
  10.         scriptContext.setAttribute("application", getServletContext(), scope);
  11.         scriptContext.setAttribute("session", request.getSession(), scope);
  12.         scriptContext.setAttribute("request", request, scope);
  13.         scriptContext.setAttribute("response", response, scope);
  14.         scriptContext.setAttribute("out", response.getWriter(), scope);
  15.         scriptContext.setAttribute("factory",
  16.                 scriptCache.getEngine().getFactory(), scope);
  17.         return scriptContext;
  18.     }
  19.     ...
  20. }

      runScript()方法(代码13)从缓存中取得被编译的脚本,然后调用eval()方法,同时把脚本环境作为参数给它。

代码13JSServletrunScript()方法

  1. public class JSServlet extends HttpServlet {
  2.     ...
  3.     protected void runScript(String uri, ScriptContext scriptContext)
  4.             throws ScriptException, IOException {
  5.         scriptCache.getScript(uri).eval(scriptContext);
  6.     }
  7.     ...
  8. }

处理请求

   我们能够简单的调用上面的runScript()方法来执行映射给HTTP请求的URL的脚本。但是,在实际的应用中,在脚本运行之前我们可能想要做一些初始化的工作,而在脚本执行完了之后做一些善后工作。

还可能在相同的环境中顺序的运行多个脚本。例如,一个脚本定义了一组变量和函数,然后另一个脚本在同一个环境中使用前面脚本中定义的变量和函数来执行一些处理。

servlethandleRequest()方法(代码14)做了以下处理:

・设定了HTTP的标题;

・运行init.jss脚本;

・从请求的URI中删除环境路径;

・执行已经取得的URI的脚本;

・运行另一个叫做finalize.jss的脚本。

代码14JSServlet类的handleRequest()方法

  1. public class JSServlet extends HttpServlet {
  2.     ...
  3.     protected void handleRequest(
  4.             HttpServletRequest request, HttpServletResponse response)
  5.             throws ServletException, IOException {
  6.         if (cacheControlHeader != null)
  7.             response.setHeader("Cache-Control", cacheControlHeader);
  8.         if (contentTypeHeader != null)
  9.             response.setContentType(contentTypeHeader);
  10.         ScriptContext scriptContext = createScriptContext(request, response);
  11.         try {
  12.             runScript("/init.jss", scriptContext);
  13.             String uri = request.getRequestURI();
  14.             uri = uri.substring(request.getContextPath().length());
  15.             try {
  16.                 runScript(uri, scriptContext);
  17.             } catch (FileNotFoundException x) {
  18.                 response.sendError(404, request.getRequestURI());
  19.             }
  20.             runScript("/finalize.jss", scriptContext);
  21.         } catch (ScriptException x) {
  22.             x.printStackTrace(response.getWriter());
  23.             throw new ServletException(x);
  24.         }
  25.     }
  26.     ...
  27. }

JSServlet类的handleRequest()方法调用了doGet()doPost()方法(代码15

代码15JSServlet类的doGet()doPost()方法。

  1. public class JSServlet extends HttpServlet {
  2.     ...
  3.     public void doGet(HttpServletRequest request, HttpServletResponse response)
  4.             throws ServletException, IOException {
  5.         handleRequest(request, response);
  6.     }

  7.     public void doPost(HttpServletRequest request, HttpServletResponse response)
  8.             throws ServletException, IOException {
  9.         handleRequest(request, response);
  10.     }

  11. }

未完,待续......

posted @ 2008-12-24 19:55  移动应用开发  阅读(227)  评论(0编辑  收藏  举报