跳到导航
BEA Dev2Dev Oracle and BEA
首页 资源中心 dev2dev学堂 在线技术论坛 User Group CodeShare
dev2dev 首页 > 资源中心 > 技术文章
在多台应用服务器中使用特定于应用服务器的功能

时间:2004-04-02
作者:Tom Stamm
浏览次数:
本文关键字:页面流tomcat struts移植"Portable Page Flow"
文章工具
推荐给朋友 推荐给朋友
打印文章 打印文章

通常情况下,设计在兼容J2EE的应用服务器上运行的程序时,还是不超出该应用服务器支持的J2EE规范为好。然而,有些时候需要使用特定于应用服务器的功能,超出规范是必然的。这样做有一个缺点,就是很难将程序移植到其他应用服务器上。本文将引入一个例子来讨论当可能需要超出J2EE servlet 规范时,采取怎样的措施以达到程序在多台应用服务器上工作的目标。

问题


WebLogic页面流提供基于struts/JSP上的框架,它可以通过在java代码中使用注释来定义webapp流。其中有些功能,尤其是安全性方面,依赖于内部的WebLogic类。为了制作可移植页面流开发工具包,我们需要建立一种机制,它可以在Tomcat上尽可能多地提供这种功能,并且在某些确实不被支持的事物出现时,使框架从容地面对失败。而且保持高透明度也很重要,这样页面流运行时就不用知道它是在什么servlet平台上运行。要想观察其运行效果,请下载Portable Page Flow工具包,并在Tomcat上运行示例页面流。

我在此将要展示的特定例子是,给出一个用户名和密码,通过程序在页面流中进行注册。用兼容J2EE servlet规范的方式是无法完成这一点的,但是,注册用户的能力又必须存在于servlet容器中的某个地方,这就是需要解决的问题,同时还要指出如何将其公开。像页面流这种情况,我们采用这样一种方式,使其不用改变主要代码,就能顺利地扩展到其他应用服务器。

解决方案——适配器层


为了达到可移植的目的,我们创建由一个接口组成的适配器层。该接口定义了在servlet 规范未公开的页面流所使用的功能:

public interface ServerAdapter
{    
    public void login( String username, String password, HttpServletRequest request )
        throws LoginException;
}


对于这个例子接口定义了一个login方法它带有用户名、密码和HttpServletRequest对象并且对该用户名/密码的组合进行认证使得此用户成为当前用户否则如果该用户名/密码的组合无效就会抛出一个javax.security.auth.login.LoginException异常。现在可以为每一个需要Web应用程序支持的特定应用服务器实现这个接口。

当这个webapp要调用该方法的时候,它仍需要知道它使用哪一个实现。这个问题可以用工厂模式来解决:建立一个知道如何查看环境、实例化和返回正确实现的工具类。可移植页面流的目标是在Tomcat上提供尽可能多的功能,并且使页面流在其他servlet容器上以受限的方式运行。为达到这个目标,我们创建了一个默认的ServerAdapter实现,它基本上完成所有方法中的无操作指令(no-op);一个tomcat 实现,它实现大多数方法;还有一个weblogic方法,它可以实现任何事情(有关受支持的详细内容,请参阅Portable Page Flow的文档)。工具类中的工厂方法通过查看系统属性,检查是否有一个属性被显式指定,然后根据各种环境变量识别出WebLogic或者Tomcat服务器,从而创建正确的应用服务器适配器。

获得过去的J2EE

接下来的问题是对其他应用服务器的内部工作进行访问。对于Tomcat来说,它具有非常严格的类加载器层次结构,这使得对于webapp中的类来说,不可能访问尚未由servlet API公开的服务器内核中的类。(要获得对其工作原理的详尽描述,请访问http://jakarta.apache.org/tomcat/tomcat-5.0-doc/class-loader-howto.html)。有一个system类加载器,它是一个common类加载器(common)的父亲,而这个common类加载器又是Catalinawebapp类加载器的父亲。Catalina有内部安全功能,因此由webapp类加载器加载的webapp无法访问它。



为了实现这种情况,我们创建一个接口,PageFlowHelper,是ServerAdapter接口的镜像,位于common区域。该接口的实现知道如何使用Catalina内部类,它位于server区域,即PageFlowHelperImpl,这通过Catalina类加载器加载。所以现在我们有了一个webapp可以访问的接口和该接口中的一个实现;但是问题仍然存在,该实现无法融入到webapp

Tomcat的一个非J2EE 特性就是“阀(valve)”的概念。这基本上允许你设置一个类的“管道”,当收到请求,可以按顺序调用这些类。每一个阀可以对请求做任何它想要做的事,而且能够将执行结果传递给下一个阀,或者在必要时停止执行。Portable Page Flow工具包提供了一种阀实现,对每一项请求,放置一个PageFlowHelperImpl的实例,而且将其放置在请求中的某个位置:

        // Initialize pageflow helper – the request and response that we have inside the valve are actually internal
        // Catalina classes that wrap the “real” HttpServletRequest and HttpServletResponse
        HttpServletRequest hreq =
            (HttpServletRequest) request.getRequest();
        PageflowHelper helper = new PageflowHelperImpl();
        // The helper keeps a handle to the internal request/response pair and the valve itself
        ((PageflowHelperImpl)helper).initRequest( (HttpRequest)request, (HttpResponse)response, this );
        hreq.setAttribute( PageflowHelper.PAGEFLOW_HELPER_KEY, helper );


现在ServerAdapterTomcat 实现可以从请求中获得这个实例PageFlowHelper一样),并且委托给它。现在,在 Page Flow内部调用Tomcat服务器中的PageFlowUtils.getServerAdapter().login( username, password, request ),它会从请求中获得PageFlowHelper对象,然后在其上调用login方法;这样就允许页面流通过类加载器边界来访问内部Tomcat功能,而无需直接访问servlet规范之外的类。

结束语


J2EE servlet规范提供了许多功能,但是它还是遗漏了不少在“现实世界”应用程序中有用的东西。在一般的情况下,还是坚持规范要更安全些,对于那些要运行在多个容器的应用程序来说,尤其如此。然而,万一要使用没有包括在规范中的功能,还是有办法合理地创建一个对应用程序的其余部分透明的可扩展解决方案。大部分servlet容器虽然在webapp环境下隐藏了许多功能,但是它们仍然被系统自身使用,而且通常情况下能够通过某种创造性的类加载程序来访问它们。
dot dot dot

dot
  作者其它文章
您对本文的评价
您对这篇文章的看法如何?
太棒了!5分 不错啊 4分 一般般 3分 有待提高 2分 不好 1分

   
相关产品