Geronimo tomcat: 在 Apache Geronimo 插件体系中将 Apache Tomcat 这个优秀的 Web 容器整合至其中

Apache Geronimo 灵活的插件体系将 Tomcat, OpenJPA, OpenEJB, ActiveMQ 等第三方组件集成至其中。本文从多角度介绍了在 Apache Geronimo 中,如何通过其微内核的设计和灵活的插件体系将 Apache Tomcat, 这个优秀的 Web 容器整合至其中, 内容涉及到集成环境下 Tomcat Web 容器的配置和启动, Geronimo 基础服务的使用, 以及 Web 应用程序的部署。 同时, 对于部分新旧版本之间实现的差异也进行了优劣比较。

Geronimo 整合机制概述

Apache Geronimo 是一款优秀的开源 Java EE 应用服务器平台。 其本身只是一个应用程序框架,但依赖其独特的微内核架构和灵活的插件机制, 整合了大量的开源产品, 例如 Tomcat, ActiveMQ, OpenEJB 等, 极大地丰富了其功能。

图片 1所示为 Geronimo 架构图, 可以看到, 处于最底层的是 Geronimo 内核, 它可以说是 Geronimo 的心脏, 处于其上的所有基础服务和各种插件都以 GBean 的形式运行于其中。 Geronimo 提供的基础服务包括日志服务, 线程池服务, 生命周期管理服务, 插件支持等, 其他插件和用户的应用程序都可以使用这些基础服务, 以提高程序开发效率。 前文提到, Geronimo 本身不提供 Java EE 的相关服务, 但通过插件集成其他组件达到对相关标准的支持。 例如, 通过 OpenEJB 插件, 提供了对 EJB 标准的支持。 通过 Tomcat 插件, 提供了对 Web 标准的支持, 本文即介绍 Tomcat 插件实现。

图 1. Geronimo 架构图

图 1. Geronimo 架构图

一般来说, Geronimo 插件由三部分内容组成, 分别是 :

  1. 需整合的第三方组件包。以 Tomcat 插件为例, 指的是 Tomcat 提供的相关类库文件。
  2. 与 Geronimo 之间的整合代码, 主要是 Geronimo Bean (Gbean)的定义。 这些 GBean 使得第三方的组件可以运行于 Geronimo 内核之中。
  3. 插件的运行时配置, 包含了在插件运行时, 需要启动的 GBean 的实例定义和其运行时参数设置等。

从插件的组成可以看到, 其关键作用的便是 GBean, 它可谓 Geronimo 插件的基石。 那么, GBean 在插件体系中, 究竟扮演了什么角色呢 ?

图 2. GBean 的角色之一 : 适配器 ( Adapter )

图 2. GBean 的角色之一 : 适配器 ( Adapter )

如 图片 2所示, GBean 的第一个角色是适配器。 在第三方组件中, 往往在运行时需要初始化一些实例, 例如 Tomcat 在运行时需要初始化多种 Connector 来接受用户的请求, 而 OpenEJB 在运行时需要初始化 EJB 容器, 以用于部署用户的 EJB 组件。 由此, 结合 Geronimo 提供的生命周期的基础服务, 可以为那些运行时需要创建的第三方应用组件定义对应的 GBean, 在这些 GBean 中封装第三方组件的创建和启动逻辑。 同时, 还可以通过 GBean 灵活的 XML 配置方式, 将参数设置传递到所封装的第三方组件中去。

图 3. GBean 的角色之二 : 代理器 ( Broker )

图 3. GBean 的角色之二 : 代理器 ( Broker )

GBean 的另外一个角色是代理器, 从 图片 3可得知, 插件之间的交互, 是通过 GBean 来实现。 同样, 如果用户希望修改一些组件的运行时参数时, 也可以通过调用 GBean 开放的接口。 例如, Geronimo 为所有的 Web 容器插件定义了添加 Web Service 的接口, 那对于 Axis2 插件而言, 无需知道该服务是由 Tomcat 插件还是 Jetty 插件提供, 它只需要到内核中检索实现该接口的 GBean, 并调用对应的方法即可。 换句话说, GBean 屏蔽了任何第三方实现相关的内容, 并依托 Geronimo 内核, 对外发布服务, 服务消费者只需要在 Geronimo 内核中检索提供所需服务的 GBean 实例, 并调用对应的方法, 而消费者自身无需了解服务提供者的任何信息, 实现了服务提供和消费的完全透明性。

以上概述了 GBean 在 Geronimo 插件体系中最常见的两种作用, 以此我们也可以得知, 在开发一个插件时, 通常需要定义两种类型的 GBean, 一种便是适配器类型, 由此实现第三方组件的生命周期管理和配置管理。 另外一种便是代理器类型, 如果我们希望对外发布一些服务接口供其他组件使用, 则需要定义对应的 GBean, 并实现公共接口。

Geronimo 与 Tomcat 整合

Apache Tomcat 是一个优秀的开源 Web 服务器, 提供了对 Servlet, JSP 等 Web 标准的实现。 Geronimo 的发行版本之一便是以 Tomcat 为 Web 容器的 Java EE 应用服务器版本。 早在 Geronimo 1.* 时代, Tomcat 就被通过插件的形式整合到 Geronimo 当中, 到目前的 Geronimo 2.2, 整合的实现发生了许多变化。 下文将分别介绍其实现, 并对不同实现进行比较。

Tomcat 容器配置和启动

在整合 Tomcat 的步骤中, 最重要的就是使 Tomcat Web 容器在 Geronimo 集成环境中启动起来。 首先来介绍一下 Tomcat Web 容器的组成, 如 图片 4所示 :

图 4. Tomcat Web 容器结构图

图 4. Tomcat Web 容器结构图

Tomcat Web 容器构成是高度结构化的, 并且每个组件都提供了可扩展结构以用于添加其子组件。 关于各个组件的含义和作用, 请参照 Tomcat 官方网站

根据前文介绍 的 GBean 作为适配器, 集成这些组件的方式之一便是为其每个组件定义对应的 GBean, 再通过 GBean 之间的引用, 将其组装起来。 在 Geronimo 1.* 和 Geronimo 2.1.* 中, 就是通过这种方式实现 Tomcat Web 容器的配置与启动。

以 Connector 组件为例, Connector 的作用是接受用户的 Web 请求, 比如说, 如果期望用户可以通过 http://localhost:8080 访问部署在 Tomcat 之中的应用程序, 则需要定义一个支持 HTTP 协议, 并绑定到 8080 端口的 Connector。

如 图片 5所示, Geronimo-Tomcat 插件为 Tomcat 支持的每种 Connector 都定义了一个 Connector GBean。 例如 Http11ConnectorGBean 为支持 HTTP 1.1 协议的连接器, Https11ConnectorGBean 为支持 HTTPS 1.1 协议的连接器等。

Geronimo 1.* 和 2.1.* 中的 Tomcat 整合实现

图 5. Geronimo 1.* 和 2.1.* 中定义的 Connector GBean 结构图

图 5. Geronimo 1.* 和 2.1.* 中定义的 Connector GBean 结构图

如 清单 1所示, 在所有类型的 Connector GBean 的父类 ConnectorGBean 中, 定义了 Tomcat 私有的 Connector 类型的成员变量, 并在构造函数中, 创建了该 connector 的实例。 而在 doStart 方法中, 首先将 connector 对象通过 container 引用添加到 Tomcat Web 容器中, 并调用 Tomcat Connector 对象自身的 start 方法。 而在 清单 2所示的 BaseHttp11ConnectorGBean 的代码片段中, 封装了 Tomcat Connector 特定的参数设置逻辑。 对于其他组件而言, 其只需从 Geronimo 内核中检索到 Connector GBean 对象, 并调用其 setAcceptCount 方法, 即可实现对 acceptCount 属性修改。

清单 1. ConnectorGBean 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public abstract class ConnectorGBean extends BaseGBean implements CommonProtocol,
    GBeanLifecycle, ObjectRetriever, TomcatWebConnector {
      ……
      protected final Connector connector;
      private final TomcatContainer container;
      ……
      public ConnectorGBean(String name, Map initParams, String tomcatProtocol,
               TomcatContainer container, ServerInfo serverInfo) throws Exception {
           ……
          // Create the Connector object
         connector = new Connector(tomcatProtocol);
         setParameters(connector, initParams);
 
}
 
public void doStart() throws LifecycleException {
container.addConnector(connector);
      connector.start();
      ……
container.addConnector(connector);
}
 
public void doStop() {
try {
           connector.stop();
      } catch (LifecycleException e) {
           Log.error(e);
      }
      container.removeConnector(connector);
   ……
  }
}
清单 2. BaseHttp11ConnectorGBean 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
public abstract class BaseHttp11ConnectorGBean extends ConnectorGBean implements
                    BaseHttp11Protocol, StatisticsProvider {
   ……
   public void setAcceptCount(int acceptCount) {
            connector.setAttribute("acceptCount", new Integer(acceptCount));
   }
    
  public void setAddress(String address) {
            connector.setAttribute("address", address);
   }
   ……
}

和上文介绍的 ConnectorGBean 类似, Geronimo-Tomcat 插件针对其他 Tomcat 组件而定义的 GBean 实现也是如此。 概而言之, 通过这些类似适配器的 GBean, 实现了 :

  • 对 Tomcat 私有对象创建和配置的逻辑的封装
  • 实现了各个组件之间的组装。伴随 GBean 的生命周期,最终在 Geronimo 环境中构建并启动了一个完整的 Tomcat Web 容器。

Geronimo 2.2 中的 Tomcat 整合实现

但是,在 Geronimo 2.2 中, 对 Tomcat 的整合方式进行了彻底修改。我们首先介绍新的 Tomcat 整合方式, 之后再与之前的方式做对比。

Tomcat 自身是通过 server.xml 来描述 Web 容器的构成和参数设置, 如 清单 3所示 :

清单 3. server.xml 片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on"/>
......
 
<Service name="Catalina">       
<Connector name="TomcatWebConnector"
                  port="${HTTPPort + PortOffset}"
                  host="${ServerHostname}"
                  protocol="HTTP/1.1"
                  connectionTimeout="20000"
                  redirectPort="${HTTPSPort + PortOffset}"
                  executor="DefaultThreadPool"/>
            ......
            <Engine name="Catalina" defaultHost="${ServerHostname}">
           <Host name="${ServerHostname}" appBase=""
                 unpackWARs="true" autoDeploy="true"
                 xmlValidation="false" xmlNamespaceAware="false">
               <Valve className="org.apache.catalina.valves.AccessLogValve"
                  directory="logs" prefix="${ServerHostname}_access_log."
                  suffix=".txt" pattern="common" resolveHosts="false"/>
           ......
       </Engine>       
</Service>
</Server>

那么, 在 Geronimo 中是否也能通过 server.xml 来定义 Web 容器的结构呢 ? 如此对于原来的 Tomcat 用户而言, 没有任何学习的曲线。 甚而, 用户可以将之前修改过 Tomcat 配置文件拷贝到指定目录, 则立即获取之前的环境。

基于以上的期望, 在 Geronimo 2.2 中, 通过如下步骤实现了 Tomcat Web 容器的整合。

  • 依据 server.xml 的样式 (schema) 文件, 利用 Java Architecture for XML Binding(JAXB),生成了对应的 Java 类, 由此实现 server.xml 文件与 Java 对象之间的转换。
  • 在每个组件对应的 JAXB 生成类中, 封装了 Tomcat 私有组件的创建和初始化逻辑, 同时组件之间的拼装逻辑也包含于此。 依旧以 Connector 为例, 如 清单 4所示, 在 getConnector 方法中, 包含了对 Tomcat 私有的 Connector 对象创建和初始化。 而组装 Connector 组件的逻辑位于其父组件 Service 之中。 在清 单 5所示的 ServiceType 的 getService 方法, 其所包含的 ConnectorType 对象的 getConnector 方法依次被调用, 并添加到 Service 组件中。
  • 定义 TomcatServerGBean, 如 清单 7所示, 配置一个 TomcatServerGBean 实例,serverConfigLocaiton 属性指定 server.xml 配置文件的相对路径。 如清单 6 所示,在 TomcatServerGBean 的构造函数中, 对 server.xml 执行解析, 并调用顶层组件 serverType 的 build 方法, 完成 Tomcat 各个组件的创建, 初始化和组装, 最终在 doStart 方法中启动容器, 开始接受用户请求。
清单 4. ConnectorType 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ConnectorType {
 ……
 @XmlAttribute
 protected String className = Connector.class.getName();
 ……
 public Connector getConnector(ClassLoader cl, Service service) throws Exception {
 ……
   ObjectRecipe recipe = new ObjectRecipe(className, properties);
     recipe.allow(Option.IGNORE_MISSING_PROPERTIES);
     recipe.setConstructorArgTypes(new Class[] { String.class });
     recipe.setConstructorArgNames(new String[] { "protocol" });
     Connector connector = (Connector) recipe.create(cl);
     ……
     return connector;
   }
}
清单 5. ServiceType 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ServiceType {
 
 @XmlElement(name = "Connector")
 protected List<ConnectorType> connector;
 public Service getService(ClassLoader cl, Kernel kernel) throws Exception {
   ……
   for (ConnectorType connectorType: getConnector()) {
     Connector connector = connectorType.getConnector(cl, service);
     service.addConnector(connector);
   }
   ……
 }
}
清单 6. TomcatServerGBean 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TomcatServerGBean implements GBeanLifecycle {
 public TomcatServerGBean( …… ) throws Exception {
   ……
   Reader in = new StringReader(serverConfig);
   try {
     ServerType serverType = loadServerType(in);           
     server = serverType.build(classLoader, kernel);
   } finally {
     in.close();
   }
 }
 public void doStart() throws Exception {
   server.initialize();
   ((Lifecycle)server).start();
 }
 ……
}
清单 7. TomcatServerGBean 配置文件片段
1
2
3
4
5
6
7
8
9
10
11
12
<gbean name="TomcatServer" class="org.apache.geronimo.tomcat.TomcatServerGBean">
<attribute name="serverConfigLocation">var/catalina/server.xml</attribute>
<reference name="ServerInfo">
<name>ServerInfo</name>
</reference>
<reference name="AttributeManager">
         <name>AttributeManager</name>
</reference>
<reference name="MBeanServerReference">
         <name>MBeanServerReference</name>
</reference>
</gbean>

Tomcat 整合实现的对比

应该说, Geronimo 1.*/2.1.* 和 Geronimo 2.* 中整合 Tomcat 的整体思路并没有发生变化, 都是通过 GBean 封装第三方组件, 并伴随 GBean 的生命周期管理启动和停止第三方组件, 同时通过 GBean 提供对其访问的窗口。 主要区别在一些实现策略以及用户可用性方面, 下文从以下几个角度对两者进行比较 :

  • 其他组件和应用获取 Tomcat 状态信息的便捷性 :
    • 在 Geronimo 1.*/2.1.* 的环境中, 为每个 Tomcat 组件都定义了对应的 GBean,例如 Connector 对应了 ConnectorGBean, Valve 对应了 ValveGBean 等, 而每个组件都通过提供公开的方法以便于其他组件和应用访问, 应该说是相当方便。
    • 在 Geronimo 2.2 的环境中, 所有的组件的创建和初始化均封装在 JAXB 类中, 而这些类并没有通过 GBean 的形式运行于内核中, 其他组件和应用访问的唯一接口只有 TomcatServerGBean, 略显单薄。 事实上, 在 Geronimo 2.2 的开发过程中, 已充分考虑到这一点, 对于旧版本中的 ConnectorGBean, ValveGBean 等绝大部分 GBean 都进行修改并予以保留, 即提供对已创建 Tomcat 组件的访问和设置。 默认情况下或者通过配置后, 这些 GBean 实例仍然会被 Geronimo 内核载入并启动。

综上而述, 就 Tomcat 状态访问的便捷性上, 考虑到 Geronimo 2.2 对之前绝大部分 GBean 予以保留, 将由于整合策略的修改而造成对访问便捷性的影响降到最低, 两者相差并不大。

  • Geronimo 客户的用户体验
    • 在 Geronimo 1.*/2.1.* 的环境中, 由于每个 Tomcat 组件都是由对应 GBean 创建和组装, 所以当用户修改 Tomcat Web 容器配置时, 需要在 var/config/config.xml 文件中, 添加或者修改对应组件的 GBean 定义。 对于原来 Tomcat 用户而言, 需要学习 GBean 的相关概念, 稍显不便。 当然, 熟悉了 GBean 的配置规则之后, 仍然是比较方便的。
    • 在 Geronimo 2.2 的环境中, 所有的 Tomcat Web 容器的配置都存在于 var/config/server.xml 中, 并且与 Tomcat 自身的配置文件保持了大部分的兼容。 毫无疑问, 对于原来 Tomcat 用户来说, 几乎可以立马上手, 根本不需要去了解 GBean 的相关概念。 有一点需要强调的是, Geronimo 对部分 server.xml 中的设置不提供支持, 比如说数据源, 安全域, 全局目录等, 原因在于, 这些配置, 均属于 Tomcat 的自定义扩展, 而 Geronimo 对此提供了更强大和完善的配置方式, 具体可参照 Geronimo 的官方文档。 而对于 Geronimo 1.*/2.1.* 的用户来说, server.xml 也显得更加简洁。 前文也提到, 考虑到兼容性的原因, 之前的 GBean 配置均予以保留, 用户依旧可以使用之前的方式配置 Tomcat Web 容器, 但 Geronimo 社区并不推荐这样做, 而且在今后的版本中有可能会移除这些 GBean 的配置方式, 以保持整体配置的唯一性。

Geronimo 基础服务整合

从 图片 1所示 Geronimo 架构图可知, Geronimo 对外提供了很多基础服务, 包含线程池服务, 安全访问控制服务等, 以下将介绍 Tomcat 整合中如何使用其中的线程池服务以提高应用程序的运行效率。

线程池服务是应用服务器的一个重要关注点, Geronimo 自产生之始, 便通过 GBean 的形式提供了线程池的支持, 其他组件或者应用可以通过该 GBean 获取线程池支持。 但在 Geronimo 1.*/2.1.* 中, Tomcat 插件并没有使用 Geronimo 平台提供的线程池服务, 而是由各个 Connector 维护了其自身的线程池。 显而易见, 资源不能得到最大化利用, 而且也不利于 Geronimo 监控当前 Web 请求的相关状态。 在 Geronimo 2.2 中, 这点做了改善, 默认情况下, 所有支持外部线程池的 Tomcat Connector 组件均会使用 Geronimo 的线程池服务。 下文将介绍在 Geronimo 2.2 中是如何配置外部线程池和其内部的一些实现。

如 清单 8所示, 如果需要在 Tomcat 中定义一个公共线程池, 需要添加 Executor 元素, 而 Executor 元素的 executor-ref 属性设定了在 Geronimo Kernel 中检索线程池服务的查询条件, 当前配置的含义是查询名称为 DefaultThreadPool 并且实现了 java.util.concurrent.Executor 接口的 GBean 对象。 由默认的查询条件可知, Geronimo 提供的线程池实现是通过名为 DefaultThreadPool 的 GBean 供其他组件和应用使用的。 显然, 用户如果配置了自定义的线程池, 可以很方便的修改 executor-ref 属性而达到目的。 Executor 元素只是线程池的定义, 需要某个在 Connector 中使用, 需要设置其 executor 属性, 值为期望使用的 Executor 元素的名称。

需要注意的两点是, executor-ref 为 Geronimo 自定义属性, 仅在 Geronimo 环境中有效。 另外一点是, 不是所有的 Tomcat Connector 类型都支持外部连接池, 具体可参照 Tomcat 官方文档。

如 清单 9所示的代码片段, 在 ExecutorType 中, 通过 executor-ref 属性指定的查询条件, 在 Geronimo 内核中执行查询, 并使用结果列表中第一个线程池 GBean 的实例。 而在 ConnectorType 中, 依据 exector 属性指定的 Executor 的名称, 从 Service 组件中获取其引用并通过反射的方式调用 Connector 所持有 ProtocolHandler 对象的 setExecutor 方法, 如此之后, 该 Connector 将使用设定的 Geronimo 线程池来处理用户请求。

清单 8. Tomcat Connector 线程池配置
1
2
3
4
5
6
7
8
9
10
11
12
13
<Service name="Catalina">
 <Executor className="org.apache.geronimo.tomcat.TomcatExecutorWrapper"
                 name="DefaultThreadPool"
                 executor-ref="?name=DefaultThreadPool#java.util.concurrent.Executor"/>
            <Connector name="TomcatWebConnector"
                  port="${HTTPPort + PortOffset}"
                  host="${ServerHostname}"
                  protocol="HTTP/1.1"
                  connectionTimeout="20000"
                  redirectPort="${HTTPSPort + PortOffset}"
                  executor="DefaultThreadPool"/>
   ……
</Service>
清单 9. Tomcat Connector 引用 Geronimo 线程池实现片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
                ConnectorType.java
 
public Connector getConnector(ClassLoader cl, Service service) throws Exception {
for (Map.Entry<QName, String> entry : otherAttributes.entrySet()) {
              String name = entry.getKey().getLocalPart();
              if (executorSupported && "executor".equals(name)) {
                    Executor executor = service.getExecutor(entry.getValue());
                    ……
                    IntrospectionUtils.callMethod1(connector.getProtocolHandler(),
                            "setExecutor",
                            executor,
                            java.util.concurrent.Executor.class.getName(),
                            cl);
 
              }
……
        }
}
 
 
ExecutorType.java
 
public Executor getExecutor(ClassLoader cl, Kernel kernel) throws Exception {
          Map<String, Object> properties = new HashMap<String, Object>();
          for (Map.Entry<QName, String> entry: otherAttributes.entrySet()) {
String name = entry.getKey().getLocalPart();
              if (name.endsWith("-ref")) {
                    AbstractNameQuery query =
                                new AbstractNameQuery(URI.create(entry.getValue()));
                    Set<AbstractName> names = kernel.listGBeans(query);
                    ……
                    Object ref = kernel.getGBean(names.iterator().next());
                    properties.put(name.substring(0, name.length() - 4), ref);
 
              }
        }
        ……
        return executor;
}

部署 Web 应用程序到 Tomcat 容器

Tomcat 插件除了实现 Tomcat 容器配置和启动以及 Geronimo 基础服务整合等功能外, 另外的一个重要功能就是如何将用户的应用程序部署到集成后的 Tomcat Web 容器中去。

在 Geronimo 整合机制概述章节中, 我们提到 GBean 的另外一个角色 : 代理器。 当用户通过控制台 GUI 界面或者命令行下执行 deploy 命令时, 需要一个负责解析用户部署应用的 GBean 服务。 在 Geronimo-Tomat 插件中, TomcatModuleBuilder GBean 便是应此目的而定义。

在介绍 TomcatModuleBuilder GBean 之前, 我们首先列出当用户部署一个应用程序至 Java EE 运行环境中, 需要分析的主要内容包括 :

  • 应用程序依赖关系, 即当前应用依赖于哪些第三方服务, 在应用的类载入路径中需要添加哪些第三方的组件包等。
  • 应用程序的配置信息, 例如上下文路径, 显示名称等。
  • 应用程序是否包含其他 Java EE 组件, 例如是否包含 JPA 应用, 是否对外发布 Web Service 应用等。

下面, 我们将结合 TomcatModuleBuilder GBean 依次介绍以上步骤在 Geronimo-Tomcat 插件中的实现。

Geronimo 会为每个用户部署的应用程序建立一个多父类载入器, 对于一个 Web 应用程序而言, 以下内容需要添加到应用程序环境中

  • 在 geronimo-web.xml 中定义的依赖关系, 如果该依赖是一个 JAR 文件, 则添加到应用程序的类载入器路径中, 如果该依赖是其他应用或者插件, 则将其对应的类载入器设为该应用类载入器的父亲。
  • 扫描 WEB-INF/lib 目录下所有的 JAR 文件, 将其加入至当前应用的类载入器路径中。
  • 如果存在 WEB-INF/classes 目录, 同样将该目录添至当前应用的类载入器路径中。
  • 将 Tomcat 插件对应的类载入器设置为该应用类载入器的父亲。

在一个 WAR 包中, web.xml 和 geronimo-web.xml 中设置大量应用相关的信息, 这些也是 TomatModuleBuilder 处理的主要内容之一。 如 清单 10所示代码片段中, 用户在 web.xml 中设置的 displayName 值会被读出, 并设置到 TomcatWebAppContextGBean 中去。

清单 10. TomcatModuleBuilder 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void addGBeans(EARContext earContext, Module module, ClassLoader cl,
                         Collection repository) throws DeploymentException {
  GBeanData webModuleData = new GBeanData(moduleName,
                                               TomcatWebAppContext.GBEAN_INFO);
   ……
         try {
             ……
             if (webApp.getDisplayNameArray().length > 0) {
                   webModuleData.setAttribute("displayName",
                               webApp.getDisplayNameArray()[0].getStringValue());
             }
   ……
 }
 } catch (DeploymentException de) {
           throw de;
         } catch (Exception e) {
           throw new DeploymentException(
                 "Unable to initialize GBean for web app " + module.getName(), e);
}

由 清单 11所示的 TomcatModuleBuilder 配置文件而知, 在运行时, 其他 Java EE 模块对应的分析器 GBean 引用会被注入至 TomcatModuleBuilder 中。 如此, Tomcat 插件即可使用其他分析器对当前应用程序进行解析, 例如 WebServiceBuilder 会检测当前应用中是否包含 Web Service 应用, PersistenceUnitBuilder 会检测当前应用中是否包含 JPA 应用等。

清单 11. TomcatModuleBuilder 配置片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<gbean name="TomcatWebBuilder"
      class="org.apache.geronimo.tomcat.deployment.TomcatModuleBuilder">
……
 <references name="WebServiceBuilder">
 <pattern>
                <name>CXFBuilder</name>
         </pattern>
         ……
 </references>
 <reference name="NamingBuilders">
         <name>NamingBuilders</name>
 </reference>
 <reference name="ClusteringBuilders">
         <name>TomcatClusteringBuilder</name>
 </reference>
 <references name="ModuleBuilderExtensions">
 <pattern>
                <name>PersistenceUnitBuilder</name>
         </pattern>
       <pattern>
                <name>MyFacesModuleBuilderExtension</name>
         </pattern>
         <pattern>
                <name>JspModuleBuilderExtension</name>
         </pattern>
       <pattern>
                <name>SecurityBuilder</name>
       </pattern>
 </references>
……
 </gbean>

在 TomcatModuleBuilder 执行完所有分析步骤之后, 最终得到的就是 清单 10中提及地 TomcatWebAppContext GBean, 它包含了所有对当前应用分析的结果。 同样, 将应用添加到 Tomcat Web 容器中的逻辑也在该 GBean 中。 如 清单 12 所示代码片段, 当 TomcatWebAppContext GBean 启动时, 会调用 TomcatContainer 的 addContext 方法, 而在 addContext 方法中, 首先创建了 Tomcat 的 Context 对象, 然后通过 Host 的 addChild 方法, 将用户的应用程序部署至 Tomcat Web 容器之中。

清单 12. TomcatWebAppContext/TomcatContainer 代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                TomcatWebAppContext.java
public class TomcatWebAppContext implements …… {
  ……
  public void doStart() throws Exception {
    ……
    container.addContext(this);
    ……
  }
  ……
}
 
TomcatContainer.java
public class TomcatContainer implements …… {
  public void addContext(TomcatContext contextInfo) throws Exception {
    Context context = createContext(contextInfo.getContextPath(),
                            contextInfo.getDocBase(), contextInfo.getClassLoader());
    ……
    host.addChild(context);
    ……
  }
  ……
}

总结

Geronimo 集成众多开源应用, 并最终成为通过 Java EE 5 认证的应用服务平台, GBean 在其中起了至关重要的作用, 本文以 Tomcat 插件为例, 介绍了集成过程中涉及到的内容, 以及不同版本中部分实现的差异。 对于 Geronimo 用户而言, 可以更加深入了解一些内部实现机理。 而对 Geronimo 插件开发人员和系统架构设计师, 也具备一定的借鉴意义。

 

相关主题

原文地址:https://www.cnblogs.com/dengyungao/p/7492950.html