📄 将 microsoft 的 internet information server 用作 java servlet 引擎(转).txt
字号:
[b]Active Server Page[/b]
当您从 ASP 中调用 servlet 时,您正是在调用 doGet 方法,并传入适当的请求和响应对象。从这时起,servlet 就具有了完全的控制权。ASP 脚本用作 servlet 的[I]引导程序[/I]。但在您可以传入请求和响应对象之前,您必须用相应的适配器类来包装它们(稍后我对此将有详细分析)。
我将从头开始讲下去。客户机要请求的 URL 看起来与 http://localhost/servlet.asp 类似。.asp 扩展名意味着所请求的文档是一个 Active Server Page 脚本。下面就是完整的 servlet.asp 脚本:[TABLE][TR][TD][PRE]dim requestAdapterset requestAdapter = getObject( "java:com.nutrio.asp.RequestAdapter" )dim responseAdapterset responseAdapter = getObject( "java:com.nutrio.asp.ResponseAdapter" )dim servletset servlet = getObject( "java:com.nutrio.servlet.HelloWorldServlet" )servlet.doGet requestAdapter, responseAdapter[/PRE][/TD][/TR][/TABLE]
分析上一段脚本,您将发现,它是从声明一个称为 requestAdapter 的变量入手的。dim 是 Visual Basic 中对变量进行声明的命令。在 Visual Basic 中,变量没有固定类型,各种变量实际上是由 Variant 对象进行包装的,这可使变量以调用代码所要求的任何一种类型(例如,数字、字符串,等等)出现。这样做确实非常方便,但有可能使代码容易混淆,而且也不安全。这正是为什么要发明“匈牙利表示法”(Hungarian Notation) 的原因(请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]参考资料[/url])。不过,这完全是另外一场争论。
声明变量以后,就应该使用 ASP 的 getObject 方法实例化您的第一个适配器类,并相应地为其赋值。getObject 方法是 IIS 版本 4 中新增的。它被称为 [I]moniker[/I] (一种 COM 对象,用于创建其它对象的实例,请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]参考资料[/url]),但它使您能够访问 Java 对象,而没有注册任何组件对象模型(COM,请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]参考资料[/url])这类令人头痛的事。然后,您依次声明、实例化响应封装并为其赋值,然后对 servlet 做同样的事情。最后,您调用 servlet 的 doGet 方法,并传入[I]改编了的[/I]请求和响应对象。
这个特定的脚本具有相当大的局限性,因为它只启动一个特定的 servlet。您很可能希望将其扩展,以启动一整套 servlet,这样您就需要做几处小小的修改。假定您的所有 servlet 都在同一个包内,您就可以将目标 servlet 的类名作为变量传递给 URL,例如 http://localhost/servlet.asp?class=HelloWorldServlet。然后您必须更改脚本的末尾来加载指定的类。下面是新的代码:[TABLE][TR][TD][PRE]dim classNameset className = Request.QueryString( "class" )dim servletset servlet = getObject( "java:com.nutrio.servlet." & className )servlet.doGet requestAdapter, responseAdapter[/PRE][/TD][/TR][/TABLE]
这就行了!您已经将 Microsoft 的 Internet Information Server 变成了 Java Servlet 引擎。正如您将在下面看到的那样,它虽然不是一个完美的引擎,但已相当接近完美。剩下要讨论的全部内容就是适配器类的本质了。
为简洁起见,在每种适配器中,我将只讨论如何实现那些较流行的方法。对流行程度的度量是以我的个人经验和看法为依据的;没有比这更科学的了(这是我引用的原话)。
[b]Microsoft 的 Java SDK[/b]
从请求的封装开始,对象必须做的第一件事情就是,获取其 ASP 对应物的一个引用。这是通过 com.ms.iis.asp 包中的 AspContext 对象完成的。您问是[I]什么[/I]包吗?对了,这里我正要解释您为什么需要安装 Microsoft 的 Java SDK。
Microsoft 的 Java SDK 可以免费下载(请参阅[url href=http://www.cn.ibm.com/developerWorks/java/jw-iis/index.shtml#resources]参考资料[/url])。要确保您获得的是最新版本,我写这篇文章时为 4.0。按照简单的安装说明进行操作,并在出现提示时重新启动(令人叹息!)。安装 SDK 之后,相应调整您的 PATH 和 CLASSPATH 环境变量。采取点聪明的小技巧,在您的系统中搜索 jview.exe 的全部实例,然后确保最新版本在您的 PATH 中最先解析。
不幸的是,Microsoft 的 Java SDK 附带的文档和样例代码中涉及 IIS/ASP 集成的内容非常少。当然有许多夸夸其谈 -- 您得到了有关这一主题的整个已编译的 HTML 文档,但在大多数地方,它给人的感觉与其说是说明性的,不如说是自相矛盾的和含混不清的。谢天谢地,SDK 的 Samples 目录中有一个 aspcomp 包,它实际上反映了 com.ms.iis.asp 包,并提供了源代码。您在安装 SDK 时确实安装了这些样例文件,对吗?这个 aspcomp 包帮助我反向设计出许多 API 逻辑。
[b]请求适配器[/b]
既然您有了可随意使用的 Microsoft 的 SDK,您就可以回头继续实现适配器类了。下面是请求适配器的最基本的版本。我已省略了包声明和 import 语句,以便您可将注意力集中在代码的实质部分。[TABLE][TR][TD][PRE]public class RequestAdapter implements HttpServletRequest{ private Request request; public RequestAdapter() { this.request = AspContext.getRequest(); }[/PRE][/TD][/TR][/TABLE]
请注意,该类引出了一个单一的 public 构造函数,它不带任何参数。这是 ASP 脚本将该类实例化为一个 moniker 所必需的(通过 getObject 方法)。此构造函数只须从 AspContext 对象取得 ASP 的请求对象的一个引用,并存储指向它的一个指针。此适配器实现 HttpServletRequest 接口,它允许您在一种真实的 servlet 环境的外观之下,将其传递给您的 servlet。
请求对象的最常用方法是 getParameter。此方法用于检索客户机预计要提供的一段数据。例如,如果客户机刚填好了一个表单并将其提交给服务器,则 servlet 将调用 getParameter 来检索每个表单项的值。
在请求对象的 ASP 版本中,Microsoft 将区分通过 GET 到达的参数和通过 POST 到达的参数,您必须分别调用 getQueryString 或 getForm。在 servlet 版本中,在请求级别上不存在这种差别,因为用 GET 模式还是用 POST 模式,是在调用 doGet 或 doPost 时才规定的。因此,当您改编 getParameter 方法时,对于所需的值,您必须既查看查询字符串,又查看表单集合。
还有一点需要注意,当参数不存在时,Microsoft 版本中将返回一个空字符串,而 Sun 版本中将返回 null。考虑到这一点,您必须检查空字符串并在相应位置使返回值为 null。[TABLE][TR][TD][PRE]public String getParameter( String str ){ String result = request.getQueryString().getString( str ); if( ( result != null ) && result.trim().equals( "" ) ) { result = request.getForm().getString( str ); if( ( result != null ) && result.trim().equals( "" ) ) { return( null ); } } return( result );}[/PRE][/TD][/TR][/TABLE]
这样做相当简单,但期望值不要太高,因为事情即将变得更为复杂。这是因为,在 servlet 中,请求对象也引出一个称为 getParameterNames 的方法,对于客户机提供的每段数据,它都会返回关键字的一个 Enumeration。如上所述,就 servlet 而论,它是一个单一入口点,但是 ASP 则要区分 GET 提供的数据和 POST 提供的数据。为了向 servlet 返回单一的 Enumeration,必须将 ASP 请求对象的查询字符串和表单集合这两种 Enumeration 组合起来。下面是我为了解决这个问题临时编写的一个方便的小工具。此工具称为 EnumerationComposite(请不要将它与 Composite 设计模式相混淆),它使用一个 RequestDictionary(ASP 版本的 Hashtable)数组,并将这两种 Enumeration 连接起来,形成一个大的 Enumeration。下面是完整的代码:[TABLE][TR][TD][PRE]public class EnumerationComposite implements Enumeration{ private RequestDictionary[] array; private int stackPointer = 0; public EnumerationComposite( RequestDictionary[] array ) { this.array = array; } public boolean hasMoreElements() { if( this.stackPointer >= this.array.length ) { return( false ); } else if( this.array[ this.stackPointer ].hasMoreItems() ) { return( true ); } else { this.stackPointer += 1; return( this.hasMoreElements() ); } } public Object nextElement() { return( this.array[ this.stackPointer ].nextItem() ); }}[/PRE][/TD][/TR][/TABLE]
现在这个工具极大地简化了您的工作。而 getParameterNames 方法就具有了类似下面的形式:[TABLE][TR][TD][PRE]public Enumeration getParameterNames(){ return( new EnumerationComposite( new RequestDictionary[] { request.getQueryString(), request.getForm() } ) );}[/PRE][/TD][/TR][/TABLE]
响应对象的下一个最常用的方法是 getSession。会话对象是另一个核心对象,它在 ASP 和 servlet 之间互为映像。因此,您提供的会话就必须拥有自己的适配器,稍后我会对此加以说明。但在我说明之前,请先看这个请求方法:[TABLE][TR][TD][PRE]public HttpSession getSession( boolean flag ){ return( new SessionAdapter() );}[/PRE][/TD][/TR][/TABLE]
在本文中,需要改写的请求对象的最后一个方法是 getCookies。顾名思义,它返回客户机所提供的 cookie 的集合。ASP 版本的 cookie 对象使我感到为难,它似乎用作自身的一个集合,但又引出了许多具有莫明其妙的功能的方法。但是我能够对脚本进行充分的剖析来改写 servlet。由于 ASP 版本中返回 Enumeration,而 servlet 版本中则返回一个数组,这样就可以使用 Vector 类中一个不常用的方法 copyInto,来实现这种转换,这是唯一的小窍门。另外请注意,由于在 com.ms.iis.asp 包和 javax.servlet.http 包中,类名是完全相同的,因此我们不得不明确指明每个 Cookie 对象的包名。代码如下:[TABLE][TR][TD][PRE]public javax.servlet.http.Cookie[] getCookies(){ Vector tmpList = new Vector(); CookieDictionary aspCookies = this.request.getCookies(); IEnumerator e = aspCookies.keys(); while( e.hasMoreItems() ) { String key = (String) e.nextItem(); String val = aspCookies.getCookie( key ).getValue(); tmpList.addElement( new javax.servlet.http.Cookie( key, val ) ); } javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie [ tmpList.size() ]; tmpList.copyInto( cookies ); return( cookies );}[/PRE][/TD][/TR][/TABLE]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -