Introduction

This describes a simple example of ClassMan descriptor. Let us assume that Phoenix has been integrated with ClassMan and that the snippet defining classloader is included in Phoenixes deployment format (the .sar file).

Let us also assume that we want to host a servlet container (like Catalina, Jo! or Jetty) in Phoenix. The servlet specification requires that the servlets are capable of "seeing" the servlet API but recomends strongly that no servlet should be able to access any container specific classes.

To satisfy this requirement we decided to place the Servlet API classes in a parent ClassLoader to the Containers ClassLoader and each Web Applications ClassLoader. ie

      
     Servlet API CL
           |
    +------+------+
    |             |
 Servlet        WebApp
Container         CL
    CL

    

This way, both the Container and the WebApp ClassLoaders will load the Servlet API from the same ClassLoader.

Unfortunately, in our case Phoenix already assembles the Servlet Container CL by default and does not give us the opportunity to construct the Servlet API CL as a parent ClassLoader. Luckily we can overide this using the ClassMan toolkit using the following configuration file.

    
<classloaders default="container" version="1.0">

  <!-- needed to run under earlier JVMs that do not include JNDI -->
  <classloader name="jndi-api" parent="*system*">
    <entry location="sar:SAR-INF/ext/jndi.jar"/>
  </classloader>

  <!--
    The actual Servlet API classLoader. Note that this does not specify
    a physical location but instead defines an extension. This allows
    the container to search for the library that best satisfies this
    extension. Usually all the extensions are stored in a central directory
    and Phoenix will search through the jars in central to find the servlet
    jar. This allows several applications to share the same jar.
  -->
  <classloader name="servlet-api" parent="*system*">
    <extension>
      <name>javax.servlet</name>
      <specification-version>2.3</specification-version>
      <vendor-id>org.apache.jakarta</vendor-id>
      <vendor-version>1.2.3.4</vendor-version>
    </extension>
  </classloader>

  <!--
    This is a special ClassLoader that merges two other
    ClassLoaders together. When you try to load a class from
    this ClassLoader, the ClassLoader will first try to load
    the class from servlet-api ClassLoader and then try to
    load the class from the jndi-api ClassLoader. This works
    fine if the ClassLoaders define disjoint sets of classes.
    ie No class should be loadable from both the servlet-api
    ClassLoader and the jndi-api ClassLoader (with the exception
    of Classes Loaded from System ClassLoader).
  -->
  <join name="common">
     <classloader-ref name="servlet-api"/>
     <classloader-ref name="jndi-api"/>
  </join>

  <!--
    This classloader is needed to join the Phoenix API
    and the Servlet API into one ClassLoader. This is needed
    because the container is built using Phoenix APIs
    but needs to share the Servlet APIs with the WebApps.
  -->
  <join name="container-base">
     <classloader-ref name="common"/>
     <classloader-ref name="*phoenix.api*"/>
  </join>

  <!--
    This classloader is the one used to actually load the
    Servlet Container. We know this as it is specified as the
    default ClassLoader in <classloaders/> element.
  -->
  <classloader name="container" parent="container-base">
    <entry location="sar:SAR-INF/classes/"/>
    <fileset dir="sar:SAR-INF/lib/">
      <include name="*.jar"/>
    </fileset>
  </classloader>

</classloaders>

The first thing you notice about this is that the ClassLoader hierarchy is much more complicated. In fact the diagram now looks like;

      
          "servlet-api"   "jndi-api"
               CL             CL
                |             |
                +------+------+
                       |
 "*phoenix.api*"    "common" CL
       |               |
       +----+   +------+------+
            |   |             |
         "container-        WebApp
             base"            CL
               CL         (This is constructed
               |           by Container but
          "container"      shown for completeness)
                CL

    

In reality we could have merged "servlet-api" and "jndi-api" into "common" but we separated them for illustration purposes.

One thing you should notice is that Phoenix exposes two predefined ClassLoaders;

  • *system* : The system ClassLoader
  • *phoenix.api* : The ClassLoader that Phoenix uses to communicate with it's hosted components.

The above demonstrates one of the most complex examples that you are likely to come across. This arose because there was multiple "containers" hosted in same ClassLoader hierarchy. The Servlet API specification requires that implementation classes not be visible to API clients. The Phoenix API specification requires the same thing.