将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎

发表于:2007-07-01来源:作者:点击数: 标签:
将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎 用 Microsoft 的 IIS 运行 Java servlet -- 而不牺牲可移植性 Thomas E. Davis Nutrio.com 首席技术官 2000 年 7 月 内容: IIS 适配器设计模式 Servlet ASP Java SDK 请求适配器 会话适
将 Microsoft 的 Internet Information Server 用作 Java servlet 引擎
用 Microsoft 的 IIS 运行 Java servlet -- 而不牺牲可移植性

Thomas E. Davis
Nutrio.com 首席技术官
2000 年 7 月

内容:


IIS
适配器设计模式
Servlet
ASP
Java SDK
请求适配器
会话适配器
响应适配器
结论
正式请求
参考资料
作者简介



您是陷在仅有 Microsoft 产品的商店中的 Java 狂热分子吗?仅使用 Microsoft 的 Internet Information Server 和纯 Java,您就可以运行 Java servlet,而无须任何第三方产品的帮助。请不要马上就放弃 Microsoft 产品 -- 不妨试试本文中说明的方法,并仍然保持为无专有权的可移植代码。

您知不知道,无须第三方产品,就可以用 Microsoft 的 Internet Information Server (IIS) 运行 JavaServlet?您的全部所需仅仅是简明的旧式 IIS 和纯 Java。尽管您需要使用 Microsoft 的 Java SDK(原因我将在下面解释),但尽可放心,您的代码将不需要任何专用权扩展,同时又保持对其它 servlet 引擎的完全可移植性。

Microsoft 的 Internet Information Server
但您为什么要做这样的傻事,让 Java servlet 运行在并非为它设计的环境中呢?首先,由于我们无法控制的情况,许多像我们这样顽固的 Java 狂热分子都陷在了仅有 Microsoft 产品的商店里。我们全都把 Linux 软件盒子藏在桌子下面,运行 IBM 最新的 JDK 和 Apache 最新的 servlet 引擎,但是,要是老板不让我们在这样一个系统上部署一些产品,这时可就真的不好过了。您当然可以找到运行于 Microsoft 平台上的商业性 servlet 引擎,但购买它们要花大把大把的钱。试一试向您的老板解释:您需要几千美金购买一种新的 Web 服务器,因为您想要废弃操作系统附带的那个免费产品(或者只将它用作简单的传递代理,这正是许多产品目前的工作方式)。然后,等您的老板诅咒完毕,您就不妨问一问自己,您是不是有点太急于放弃 Microsoft 的产品了?Microsoft 和 Sun 都有他们的问题,但这并不能改变这样一个事实:IIS 是一种值得尊敬的软件。并且既然您知道它可以运行 Java servlet,它的吸引力也就更大了一点。

适配器设计模式
将这两种技术粘合在一起的魔法是适配器设计模式的一种简单的应用程序。不妨引用一下一个不大出名的四人团伙(Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)所写的书《Design Patterns: Elements of Reusable Object-Oriented Software》(参考资料),里面说:适配器模式的意图就是将一个类的接口转换为客户机期望的另一种接口。但哪些类是您必须改编的呢?答案是 Java Servlet 用来与其环境交互的几个核心类 -- 尤其是 Request、Response 和 Session 对象。幸运的是,您不必改编 Cookie 类 -- 这一转换将由其它适配器内部处理。

IIS,或者更具体地说,它的 Active Server Page (ASP) 环境,包含了一组核心类,它们实际上反映了 Java Servlet 规范的类。事实上我应该说,servlet 反映了 ASP 的框架,因为 IIS 在 servlet 规范制定以前早就有了产品,但我不想在 Microsoft 与 Sun 之间的争论中推波助澜。

两种框架中都存在 Request、Response、Session 和 Cookie 对象。唯一的问题是,这些对象的接口在两种环境之间不兼容。这正是适配器设计模式起作用的地方。您必须改编(或包装)对象的 IIS 版本,使它们的外观和行为都类似于 servlet 版本。

一个快捷而不够清晰的 servlet 概览
按最低要求,servlet 只须实现单个方法: public void doGet( HttpServletRequest request, HttpServletResponse response );




从技术上讲,如果 servlet 希望处理的客户机请求,使用的是 HTTP 的 POST 命令而不是 GET 命令,则 servlet 还必须实现 doPost 方法。但为了使本文保持简洁,您可以假定所有客户机请求都属于 GET 类型。

doGet 方法取得两个对象:请求和响应。请求对象将客户机发送给服务器的所有数据,以及有关客户机本身的一些元信息,都封装在一起。使用响应对象将数据发回客户机。这是非常抽象的说明,但本文并不是 servlet 的引论,所以我不想涉及更多的细节。有关 servlet 的优秀初级读本,我推荐 Jason Hunter,William Crawford 和 Paula Ferguson 合著的《Java Servlet Programming》 (O@#Reilly & Associates)。

Active Server Page
当您从 ASP 中调用 servlet 时,您正是在调用 doGet 方法,并传入适当的请求和响应对象。从这时起,servlet 就具有了完全的控制权。ASP 脚本用作 servlet 的引导程序。但在您可以传入请求和响应对象之前,您必须用相应的适配器类来包装它们(稍后我对此将有详细分析)。

我将从头开始讲下去。客户机要请求的 URL 看起来与 http://localhost/servlet.asp 类似。.asp 扩展名意味着所请求的文档是一个 Active Server Page 脚本。下面就是完整的 servlet.asp 脚本:

dim requestAdapter
set requestAdapter = getObject( "java:com.nutrio.asp.RequestAdapter" )

dim responseAdapter
set responseAdapter = getObject( "java:com.nutrio.asp.ResponseAdapter" )

dim servlet
set servlet = getObject( "java:com.nutrio.servlet.HelloWorldServlet" )

servlet.doGet requestAdapter, responseAdapter



分析上一段脚本,您将发现,它是从声明一个称为 requestAdapter 的变量入手的。dim 是 Visual Basic 中对变量进行声明的命令。在 Visual Basic 中,变量没有固定类型,各种变量实际上是由 Variant 对象进行包装的,这可使变量以调用代码所要求的任何一种类型(例如,数字、字符串,等等)出现。这样做确实非常方便,但有可能使代码容易混淆,而且也不安全。这正是为什么要发明“匈牙利表示法”(Hungarian Notation) 的原因(请参阅参考资料)。不过,这完全是另外一场争论。

声明变量以后,就应该使用 ASP 的 getObject 方法实例化您的第一个适配器类,并相应地为其赋值。getObject 方法是 IIS 版本 4 中新增的。它被称为 moniker (一种 COM 对象,用于创建其它对象的实例,请参阅参考资料),但它使您能够访问 Java 对象,而没有注册任何组件对象模型(COM,请参阅参考资料)这类令人头痛的事。然后,您依次声明、实例化响应封装并为其赋值,然后对 servlet 做同样的事情。最后,您调用 servlet 的 doGet 方法,并传入改编了的请求和响应对象。

这个特定的脚本具有相当大的局限性,因为它只启动一个特定的 servlet。您很可能希望将其扩展,以启动一整套 servlet,这样您就需要做几处小小的修改。假定您的所有 servlet 都在同一个包内,您就可以将目标 servlet 的类名作为变量传递给 URL,例如 http://localhost/servlet.asp?class=HelloWorldServlet。然后您必须更改脚本的末尾来加载指定的类。下面是新的代码:


dim className
set className = Request.QueryString( "class" )

dim servlet
set servlet = getObject( "java:com.nutrio.servlet." & className )

servlet.doGet requestAdapter, responseAdapter


这就行了!您已经将 Microsoft 的 Internet Information Server 变成了 Java Servlet 引擎。正如您将在下面看到的那样,它虽然不是一个完美的引擎,但已相当接近完美。剩下要讨论的全部内容就是适配器类的本质了。

为简洁起见,在每种适配器中,我将只讨论如何实现那些较流行的方法。对流行程度的度量是以我的个人经验和看法为依据的;没有比这更科学的了(这是我引用的原话)。

Microsoft 的 Java SDK
从请求的封装开始,对象必须做的第一件事情就是,获取其 ASP 对应物的一个引用。这是通过 com.ms.iis.asp 包中的 AspContext 对象完成的。您问是什么包吗?对了,这里我正要解释您为什么需要安装 Microsoft 的 Java SDK。

Microsoft 的 Java SDK 可以免费下载(请参阅参考资料)。要确保您获得的是最新版本,我写这篇文章时为 4.0。按照简单的安装说明进行操作,并在出现提示时重新启动(令人叹息!)。安装 SDK 之后,相应调整您的 PATH 和 CLASSPATH 环境变量。采取点聪明的小技巧,在您的系统中搜索 jview.exe 的全部实例,然后确保最新版本在您的 PATH 中最先解析。

不幸的是,Microsoft 的 Java SDK 附带的文档和样例代码中涉及 IIS/ASP 集成的内容非常少。当然有许多夸夸其谈 -- 您得到了有关这一主题的整个已编译的 HTML 文档,但在大多数地方,它给人的感觉与其说是说明性的,不如说是自相矛盾的和含混不清的。谢天谢地,SDK 的 Samples 目录中有一个 aspcomp 包,它实际上反映了 com.ms.iis.asp 包,并提供了源代码。您在安装 SDK 时确实安装了这些样例文件,对吗?这个 aspcomp 包帮助我反向设计出许多 API 逻辑。

请求适配器
既然您有了可随意使用的 Microsoft 的 SDK,您就可以回头继续实现适配器类了。下面是请求适配器的最基本的版本。我已省略了包声明和 import 语句,以便您可将注意力集中在代码的实质部分。

public class RequestAdapter implements HttpServletRequest
{
    private Request request;

    public RequestAdapter()
    {
        this.request = AspContext.getRequest();
    }


请注意,该类引出了一个单一的 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。


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 );
}


这样做相当简单,但期望值不要太高,因为事情即将变得更为复杂。这是因为,在 servlet 中,请求对象也引出一个称为 getParameterNames 的方法,对于客户机提供的每段数据,它都会返回关键字的一个 Enumeration。如上所述,就 servlet 而论,它是一个单一入口点,但是 ASP 则要区分 GET 提供的数据和 POST 提供的数据。为了向 servlet 返回单一的 Enumeration,必须将 ASP 请求对象的查询字符串和表单集合这两种 Enumeration 组合起来。下面是我为了解决这个问题临时编写的一个方便的小工具。此工具称为 EnumerationComposite(请不要将它与 Composite 设计模式相混淆),它使用一个 RequestDictionary(ASP 版本的 Hashtable)数组,并将这两种 Enumeration 连接起来,形成一个大的 Enumeration。下面是完整的代码:

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() );
    }
}

未完.....

原文转自:http://www.ltesting.net