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

发表于:2007-07-01来源:作者:点击数: 标签:
现在这个工具极大地简化了您的工作。而 getParameterNames 方法就具有了类似下面的形式: public Enumeration getParameterNames() { return( new EnumerationComposite( new RequestDictionary[] { request.getQueryString(), request.getForm() } ) ); }
 

现在这个工具极大地简化了您的工作。而 getParameterNames 方法就具有了类似下面的形式:

public Enumeration getParameterNames()
{
    return(
        new EnumerationComposite(
            new RequestDictionary[] {
                request.getQueryString(),
                request.getForm() } ) );
}


响应对象的下一个最常用的方法是 getSession。会话对象是另一个核心对象,它在 ASP 和 servlet 之间互为映像。因此,您提供的会话就必须拥有自己的适配器,稍后我会对此加以说明。但在我说明之前,请先看这个请求方法:

public HttpSession getSession( boolean flag )
{
    return( new SessionAdapter() );
}


在本文中,需要改写的请求对象的最后一个方法是 getCookies。顾名思义,它返回客户机所提供的 cookie 的集合。ASP 版本的 cookie 对象使我感到为难,它似乎用作自身的一个集合,但又引出了许多具有莫明其妙的功能的方法。但是我能够对脚本进行充分的剖析来改写 servlet。由于 ASP 版本中返回 Enumeration,而 servlet 版本中则返回一个数组,这样就可以使用 Vector 类中一个不常用的方法 copyInto,来实现这种转换,这是唯一的小窍门。另外请注意,由于在 com.ms.iis.asp 包和 javax.servlet.http 包中,类名是完全相同的,因此我们不得不明确指明每个 Cookie 对象的包名。代码如下:

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



会话适配器
现在,既然完成了请求适配器,就需要退回去讨论会话适配器。无论在 ASP 中还是在 servlet 中,会话实际上是一个散列表,您只是将对象置入会话或者从会话获取对象。这些值的用法几乎就等同于相应的响应参数规则,这些规则上面已有讨论。会话适配器的实现很复杂,这里就不介绍了。完整的源代码可从参考资料中获得。

响应适配器
下一个主要的难题是响应适配器。正像请求适配器一样,响应适配器也需要几个巧妙的办法。但是在讨论较难的内容之前,我先偏离正题讨论一点较容易的内容。下面是两个较流行的响应方法的极简单的代码:

public void sendRedirect( String str )
{
    this.response.redirect( str );
}

public void setContentType( String str )
{
    // ASP 自动设置内容的类型!
}


setContentType 用来干什么?它什么事情也没做!没错,IIS 毕竟没有创建出完美的 servlet 引擎。到执行 servlet 时,ASP 引擎已定义了内容类型以及其它标准的 HTTP 标头。但是按经验来讲,多数 servlet 不需要将内容类型设置为纯文本或 HTML 以外的任何其它类型。

正如前面提到的那样,您并不需要一个适配器来处理 cookie。响应对象的 addCookie 方法只须根据所提供的 Sun 的 cookie 的内容,来创建 Microsoft 的 cookie 的实例。Microsoft 和 Sun 都同意,cookie 只不过是将数据的名称和值配成对而已。但是,双方对于 API 中应如何表示 cookie 有效期的方式,则意见不同。

Sun 版本中表示 cookie 有效期的方法是使用一个整型值,它指定 cookie 的最长寿命(以秒为单位)。此值被传递给 Cookie 对象的 setMaxAge 方法。零值表示立即到期,而负值(一种特殊情况)表示 cookie 应在用户浏览器退出时废弃。

Microsoft 版本表示 cookie 有效期的方式有所不同。缺省情况下,Microsoft 的 cookie 设置为在用户浏览器退出时到期。因此,如果 Sun 版本中 cookie 具有负的有效期,转为 Microsoft 版本时不做修改;如果 Sun 版本中 cookie 的最长有效期大于或等于零,须将此有效期转换为 Microsoft 的 Time 对象,并将其传递给 Microsoft 版本的 cookie 对象,作为有效期。请注意,月份值在 Java 的 Calendar 类中是从零开始的,而在 Microsoft 的 Time 类中是从 1 开始的,所以在转换时必须将此值加 1。

public void addCookie( javax.servlet.http.Cookie cookie )
{
    com.ms.iis.asp.Cookie aspCookie = this.response.getCookies().getCookie
        ( cookie.getName() );
    aspCookie.setValue( cookie.getValue() );

    int age = cookie.getMaxAge();

    if( age < 0 )
    {
        // expire on browser exit
    }
    else
    {
        GregorianCalendar date = new GregorianCalendar();
        Date time = new Date( System.currentTimeMillis() + ( 1000 * age ) );
        date.setTime( time );

        Time aspTime = new Time(
            date.get( Calendar.YEAR ),
            1 + date.get( Calendar.MONTH ),
            date.get( Calendar.DAY_OF_MONTH ),
            date.get( Calendar.HOUR ),
            date.get( Calendar.MINUTE ),
            date.get( Calendar.SECOND )
        );

        aspCookie.setExpires( aspTime );
    }
}


最流行的响应方法碰巧也是最难以实现的,这正是我将它留到最后的原因。我所指的方法就是 getWriter。此方法返回 PrintWriter 对象,它让 servlet 将信息写到客户机的显示屏上。在大多数情况下,servlet 只是编制 HTML,HTML 在全部被发送到客户机之前将被存入缓冲区中。为什么要存入缓冲区呢?因为 servlet 在将大量信息转储到 PrintWriter 以后,servlet 可能通过调用 sendRedirect 方法判断出有某种出错和异常终止的情况。重定向代码一定是浏览器接收的第一条信息;显然,一旦发出了重定向,就再不需要将任何缓冲的信息发送给客户机了。

考虑到以上情况,您就必须再创建一个适配器类。这个新的适配器将封装 PrintWriter 对象。它将把它的所有内容都存入缓冲区中,直到调用了 close 方法为止。下面是相应的响应方法:


public PrintWriter getWriter()
{
    return( new PrintWriterAdapter() );
}


而下面是 PrintWriter 适配器的完整代码:

public class PrintWriterAdapter extends PrintWriter
{
    private static final String CR = "\n";

    private StringBuffer sb = new StringBuffer();

    public PrintWriterAdapter()
    {
        super( System.err );
    }

    public void print ( String str ){ sb.append( str ); }//response.write
        ( str ); }
    public void println( String str ){ print ( str + CR ); }

    public void print ( Object obj ){ print ( obj.toString() ); }
    public void println( Object obj ){ println( obj.toString() ); }

    public void print ( char[] chr ){ print ( new String( chr ) ); }
    public void println( char[] chr ){ println( new String( chr ) ); }

    public void close()
    {
        AspContext.getResponse().write( sb.toString() );
    }
}



结论
Microsoft 的 Internet Information Server 没有创造出完美的 servlet 引擎,但它已经相当接近完美了。根据我有关 servlet 的全部经验,IIS 和这些适配器类的组合已表明足以满足开发和部署商业性应用程序的需要。如果您碰巧被锁进只能选 Microsoft 产品的商店,这些工具能帮您另辟蹊径,领略 Java servlet 的奇妙之处。同时,我将一如既往地乐意倾听您的评论、批评以及改进这些代码的comments。

我在本文中引入的所有类的源代码,包括一些我没有谈及的功能,都可以在参考资料中找到。请注意,许多方法(尤其是那些我还不需要的方法)仍未实现。如果您敢于完成这一工作,请给我发一份副本(眨眼)。

对 Microsoft 或有帮助的读者的一项正式请求
我已将本文中说明的技术成功地部署到我实验室的大多数系统上。但它在几台机器上却就是不能工作。ASP 页对于对适配器对象的任何引用都报告这一条错误: "No object for moniker"。这无疑是由 Microsoft 的 Java SDK 4.0、 Microsoft 的 Internet Information Server (Windows NT Option Pack 4)、Visual J++ 和某个 Service Pack 的莫明其妙的组合引起的。我搜索了 Microsoft Developer@#s Network (MSDN),但徒劳无益,我已无计可施了。如果您知道问题所在并有解决办法,请拿出来与我分享。谢谢。

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